Подпроцесс Python.check_call против.check_output
Мой скрипт на python (python 3.4.3) вызывает скрипт bash через подпроцесс:
import subprocess as sp
res = sp.check_output("bashscript", shell=True)
Bashscript содержит следующую строку:
ssh -MNf somehost
который открывает общее мастер-соединение с некоторым удаленным хостом, чтобы разрешить некоторые последующие операции.
При выполнении скрипта Python он запросит пароль для ssh
строка, но затем блокируется после ввода пароля и никогда не возвращается. Когда я нажимаю Ctrl-C для завершения сценария, я вижу, что соединение было установлено правильно (так ssh
линия была успешно выполнена).
У меня нет этой проблемы блокировки при использовании check_call
вместо check_output
, но check_call
не получает стандартный вывод. Я хотел бы понять, что именно вызывает поведение блокировки check_output
, вероятно, связано с некоторой тонкостью с ssh -MNf
,
1 ответ
check_call()
возвращается, как только /bin/sh
процесс завершается без ожидания процессов-потомков.
check_output()
ждет, пока весь вывод не будет прочитан. Если ssh
наследует трубу тогда check_output()
будет ждать, пока он не выйдет (пока он не закроет свои унаследованные концы каналов).
check_call()
пример кода:
#!/usr/bin/env python
import subprocess
import sys
import time
start = time.time()
cmd = sys.executable + " -c 'import time; time.sleep(2)' &"
subprocess.check_call(cmd, shell=True)
assert (time.time() - start) < 1
Вывод не читается; check_call()
возвращается сразу, не дожидаясь внушительного фонового процесса Python.
check_call()
просто Popen().wait()
, Popen()
запускает внешний процесс и сразу возвращается, не дожидаясь его выхода. .wait()
собирает статус выхода для процесса - он не ждет других (внуков) процессов.
Если выходные данные считываются (они перенаправляются, и процесс Python внука наследует канал stdout):
start = time.time()
subprocess.check_output(cmd, shell=True)
assert (time.time() - start) > 2
затем он ожидает, пока не завершится фоновый процесс python, унаследовавший канал.
check_output()
звонки Popen().communicate()
, чтобы получить вывод. .communicate()
звонки .wait()
то есть внутренне, check_output()
также ожидает выхода оболочки и check_output()
ждет EOF.
Если внук не наследует трубу, то check_output()
не ждет этого:
start = time.time()
cmd = sys.executable + " -c 'import time; time.sleep(2)' >/dev/null &"
subprocess.check_output(cmd, shell=True)
assert (time.time() - start) < 1
Выход внука перенаправляется на /dev/null
то есть, он не наследует канал родителя и, следовательно, check_output()
может выйти, не дожидаясь этого.
Замечания: &
в конце, который помещает процесс Python внука в фоновом режиме. Это не будет работать в Windows, где shell=True
начинается cmd.exe
по умолчанию.