Python子进程.check_call vs .check_output

Kev*_* S. 17 python ssh bash subprocess

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

import subprocess as sp
res = sp.check_output("bashscript", shell=True)
Run Code Online (Sandbox Code Playgroud)

bashscript包含以下行:

ssh -MNf somehost
Run Code Online (Sandbox Code Playgroud)

这将打开与某个远程主机的共享主连接,以允许一些后续操作.

执行python脚本时,它将提示该ssh行的密码,但在输入密码后它会阻塞,并且永远不会返回.当我按ctrl-C终止脚本时,我看到连接已正确建立(因此ssh行已成功执行).

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

jfs*_*jfs 42

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
Run Code Online (Sandbox Code Playgroud)

输出未读取; 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
Run Code Online (Sandbox Code Playgroud)

然后它等待,直到继承管道的后台python进程退出.

check_output()调用Popen().communicate(),获取输出.内部.communicate()调用.wait()ie,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
Run Code Online (Sandbox Code Playgroud)

孙子的输出被重定向到/dev/nullie,它不会继承父管道,因此check_output()可以在不等待它的情况下退出.

注意:&最后将孙子python进程放入后台.它在默认shell=True启动cmd.exe的Windows上不起作用.