Python 子进程 .check_call 与 .check_output

Python subprocess .check_call vs .check_output

我的 python 脚本 (python 3.4.3) 通过子进程调用 bash 脚本:

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

bash脚本包含以下行:

ssh -MNf somehost

它打开一个到某个远程主机的共享主连接以允许一些后续操作。

当执行 python 脚本时,它会提示输入 ssh 行的密码,但在输入密码后它会阻塞,而不会 returns。当我按 ctrl-C 终止脚本时,我看到连接已正确建立(因此 ssh 行已成功执行)。

当我使用 check_call 而不是 check_output 时,我没有遇到这个阻塞问题,但是 check_call 没有检索标准输出。我想了解究竟是什么导致 check_output 的阻塞行为,可能与 ssh -MNf.

的一些微妙之处有关

check_call() returns 一旦 /bin/sh 进程退出而不等待后代进程(假设 shell=True 与您的情况一样)。

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() returns 立即不等待孙子后台 python 进程。

check_call() 就是 Popen().wait()Popen() 启动外部进程并立即 returns 而不等待它退出。 .wait() 收集进程的退出状态——它不等待其他(孙)进程。

如果输出被读取(它被重定向并且孙子 python 进程继承标准输出管道):

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

然后等待继承管道的后台 python 进程退出。

check_output() 调用 Popen().communicate() 以获取输出。 .communicate() 在内部调用 .wait() 即,check_output() 也等待 shell 退出并且 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