2016-03-23 2 views
6

Mein Python-Skript (Python 3.4.3) ruft ein Bash-Skript über subprocess:Python subprocess .check_call vs .check_output

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

Die bashscript enthält die folgende Zeile:

ssh -MNf somehost 

die öffnet eine gemeinsame Master-Verbindung zu einem Remote-Host, um einige nachfolgende Operationen zu ermöglichen.

Bei der Ausführung des Python-Skripts wird zur Eingabe des Kennworts für die Zeile ssh aufgefordert. Nach Eingabe des Kennworts wird die Eingabe jedoch gesperrt, und das Kennwort wird nicht mehr zurückgegeben. Wenn ich Ctrl-C, um das Skript zu beenden, sehe ich, dass die Verbindung ordnungsgemäß hergestellt wurde (so ssh Zeile wurde erfolgreich ausgeführt).

Ich habe dieses Blockierungsproblem nicht, wenn Sie check_call anstelle von check_output verwenden, aber check_call ruft stdout nicht ab. Ich würde gerne verstehen, was genau das Blockierungsverhalten für check_output verursacht, wahrscheinlich im Zusammenhang mit etwas Subtilität mit ssh -MNf.

Antwort

19

check_call() kehrt zurück, sobald /bin/sh Prozess beendet wird, ohne auf nachkommende Prozesse zu warten.

check_output() wartet bis alle Ausgaben gelesen sind. Wenn ssh die Pipe erbt, wartet check_output(), bis es beendet wird (bis es seine geerbte Pipe schließt).

check_call() Codebeispiel:

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

Der Ausgang nicht gelesen wird; check_call() kehrt sofort zurück, ohne auf den Enkel-Hintergrund-Python-Prozess zu warten.

check_call() ist nur Popen().wait(). Popen() startet den externen Prozess und kehrt sofort zurück, ohne darauf zu warten, dass es beendet wird. .wait() sammelt den Exit-Status für den Prozess - es wartet nicht auf andere (Enkel-) Prozesse.

Wenn die Ausgabe gelesen wird (es umgeleitet wird und die Enkel Python Prozeß erbt die stdout Rohr):

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

dann wartet sie, bis der Prozess Hintergrund Python, dass das Rohr austritt geerbt.

check_output() Aufrufe Popen().communicate(), um die Ausgabe zu erhalten. .communicate() Aufrufe .wait() intern d. H., check_output() wartet auch auf die Shell zu beenden und check_output() wartet auf EOF.

Wenn der Enkel das Rohr nicht erbt dann check_output() wartet nicht, es:

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 

Enkel Ausgang /dev/null dh umgeleitet wird, ist es nicht die Rohr Eltern erbt und check_output() kann deshalb verlassen ohne darauf zu warten.

Hinweis: & am Ende, die den Enkel Python-Prozess in den Hintergrund stellt. Es funktioniert nicht unter Windows, wo shell=True standardmäßig cmd.exe startet.

+0

Große Erklärung. –