2016-03-23 46 views
6

mój skrypt Pythona (Python 3.4.3) wywołuje skrypt bash poprzez podproces:Pythona podproces .check_call vs .check_output

import subprocess as sp 
res = sp.check_output("bashscript", shell=True) 

bashscript zawiera następujący wiersz:

ssh -MNf somehost 

który otwiera wspólne połączenie główne z jakimś zdalnym hostem, aby umożliwić pewne kolejne operacje.

Podczas wykonywania skryptu python, monituje o podanie hasła dla linii ssh, ale blokuje po wprowadzeniu hasła i nigdy nie zwraca. Kiedy ctrl-C kończę skrypt, widzę, że połączenie zostało poprawnie ustanowione (tak, że linia została pomyślnie wykonana).

Nie mam tego problemu z blokowaniem, gdy używam check_call zamiast check_output, ale check_call nie pobiera standardowego wyjścia. Chciałbym zrozumieć, co dokładnie powoduje zachowanie blokujące dla check_output, prawdopodobnie związane z pewną subtelnością z ssh -MNf.

Odpowiedz

19

check_call() powraca po zakończeniu procesu /bin/sh bez czekania na proces potomka.

check_output() czeka, aż wszystkie dane wyjściowe zostaną odczytane. Jeśli ssh odziedziczy rurę, to check_output() będzie czekał aż się zakończy (dopóki nie zamknie odziedziczonych końców rur). Przykładem

check_call() Kod:

#!/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 

Wyjście nie jest odczytywany; check_call() zwraca natychmiast, nie czekając na proces python w tle wnuka.

check_call() to tylko Popen().wait(). Popen() uruchamia proces zewnętrzny i natychmiast wraca, nie czekając na jego zakończenie. .wait() zbiera status wyjścia dla procesu - nie czeka na inne procesy (wnuki).

Jeśli wyjście jest odczytywany (to jest przekierowywany i proces wnuk dziedziczy pyton rurę stdout):

start = time.time() 
subprocess.check_output(cmd, shell=True) 
assert (time.time() - start) > 2 

następnie czeka, aż proces Pythona w tle, który odziedziczył wyloty rur.

check_output() dzwoni Popen().communicate(), aby uzyskać dane wyjściowe. .communicate() dzwoni .wait() wewnętrznie tj.również czeka na wyjście powłoki i check_output() czeka na EOF.

Jeśli wnuczek nie dziedziczy rurę następnie check_output() nie czeka na niego:

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 

wyjście wnuczek jest przekierowany do /dev/null znaczy, że nie dziedziczy rurę rodzica i dlatego check_output() może wyjść nie czekając na to.

Uwaga: & na końcu, który umieszcza proces pythona wnuka w tle. Nie działa w systemie Windows, w którym domyślnie shell=True domyślnie zaczyna się cmd.exe.

+0

Świetne wyjaśnienie. –