My python script(python 3.4.3)は、サブプロセスを介してbashスクリプトを呼び出します。
import subprocess as sp
res = sp.check_output("bashscript", Shell=True)
bashscriptには次の行が含まれます。
ssh -MNf somehost
これにより、リモートホストへの共有マスター接続が開かれ、後続の操作が許可されます。
pythonスクリプトを実行すると、ssh
行のパスワードを求めるプロンプトが表示されますが、パスワードが入力された後はブロックされ、戻ることはありません。スクリプト、接続が適切に確立されたことがわかります(そのためssh
行が正常に実行されました)。
check_call
の代わりにcheck_output
を使用する場合、このブロッキングの問題はありませんが、check_call
はstdoutを取得しません。 check_output
のブロック動作の原因を正確に理解したいのですが、おそらくssh -MNf
の微妙な問題に関連しています。
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プロセスをバックグラウンドに配置する最後の&
。デフォルトではShell=True
がcmd.exe
を開始するWindowsでは動作しません。