Как передать содержимое переменной через два связанных канала в python?

#python #subprocess #pipe

#python #подпроцесс #канал

Вопрос:

Мне успешно удалось передать переменную команде, которая далее не передается по каналу, таким образом:

 from subprocess import Popen, PIPE

def exec_command(command, some_input):
    proc = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
    (stdout, stderr) = proc.communicate(input=some_input)
  

… но при попытке передать его в другую переданную по каналу команду (например, в tar, а затем в split), похоже, это не работает:

 from subprocess import Popen, PIPE

def exec_piped_command(command1, command2, some_input):
    proc1 = Popen(command1, stdout=PIPE, stdin=PIPE)
    proc2 = Popen(command1, stdin=proc1.stdout, stdout=PIPE)
    (stdout, stderr) = proc2.communicate(input=some_input)[0]
  

Итак, как правильно выполнить этот второй вариант? Похоже, проблема с приведенным выше кодом заключается в том, что входные данные в команде «proc2.communicate()» не достигают канала stdin proc1? (Хотя и не уверен… к сожалению, я немного запутался в синтаксисе подпроцесса …).

Комментарии:

1. proc2.communicate пытается передать некоторые входные данные в канал, но канал ввода для proc2 был предоставлен каналом вывода из proc1 — Каналы Unix не поддаются нескольким процессам записи / чтения (они НЕ похожи на объекты очереди; ни на сокеты). Вероятно, это приводит к тупиковой ситуации в вашем коде.

2. Да, верно, итак, мой вопрос в том, как мне передать переменную на вход proc1 и при этом выполнить всю связанную команду? … выполнение proc1.communicate(input=some_input) также не работает …

3. Стилистическая проблема; from x import * действительно плоха и не поощряется все время.

4. Вы могли бы попробовать proc1.stdin.write(…) … однако это может привести к взаимоблокировке при заполнении буфера. Вы могли бы попробовать операции fnctl, чтобы перевести proc1.stdin в неблокирующий режим (и обернуть попытки write() в соответствующие наборы обработки исключений для обработки результатов «EWOULDBLOCK»). Если вы это сделаете, то, вероятно, вам придется выполнять аналогичные неблокирующие манипуляции с стандартным выводом proc2 и использовать для этого вызовы read(), а не вызывать .communicate() вообще. Что касается КАНАЛА между proc1 и proc2 … ваш процесс Python сохраняет копию того, что для вас бесполезно и должно быть закрыто.

5. Примечание: вы не можете сделать ничего полезного с семантикой блокировки между proc1 и proc2 … но они не должны заходить в тупик, пока у вас нет блокирующего и полного буфера на обоих концах конвейера. Если вам нужны более сложные взаимодействия между процессами (например, чтобы превратить их в совместные процессы), тогда вам пришлось бы вставлять свои собственные процессы (Python) для активной ретрансляции данных из одного выходного канала в другой входной канал и так далее — или вам понадобились бы документированные способы ограничить эти процессы семантикой буферизации друг друга.

Ответ №1:

Одной из возможностей было бы настроить всю команду для выполнения оболочкой ( shell=True среди аргументов ключевого слова вашего Popen() вызова … и только .communicate() с концами всего конвейера (ваш ввод поступает в стандартный файл command1, а ваш стандартный вывод / stderr поступает из стандартного вывода / stderr command2).

Более сложный, но детализированный подход заключался бы в использовании вызовов os.fork() along with os.pipe() и os.dup2() и, возможно, некоторых os.fcntl() для настройки ваших собственных подпроцессов с вашим собственным заполнением и любыми из требуемых характеристик блокировки / неблокируемости, которые вам нужны в ваших файловых дескрипторах, а затем, наконец, с использованием ваших собственных os.exec* функций в каждом из них.

Очевидно, что этот последний подход будет дублировать значительную часть кода, который уже есть в subprocess модуле. Однако это предоставляет вам возможность выполнять некоторые из этих шагов по-другому способами, которые не доступны через subprocess.Popen класс.

Средним путем было бы создать подкласс, который наследует от subprocess.Popen

Конечно, может быть предпочтительнее выполнять некоторые части этого конвейера с помощью кода и модулей Python (таких как операции tar, gzip и split).

Комментарии:

1. Спасибо, я выбрал простое решение с shell = True. К сожалению, это немного менее безопасно, но я надеюсь, что все будет в порядке, если не добавлять в команду никаких строк, управляемых пользователем …