Подпроцесс 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 по умолчанию.

Другие вопросы по тегам