Python 子进程 .check_call 与 .check_output

Posted

技术标签:

【中文标题】Python 子进程 .check_call 与 .check_output【英文标题】:Python subprocess .check_call vs .check_output 【发布时间】:2016-07-10 05:40:20 【问题描述】:

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

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

bashscript 包含以下行:

ssh -MNf somehost

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

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

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

【问题讨论】:

【参考方案1】:

check_call()/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() 立即返回,无需等待孙子后台python进程。

check_call() 只是Popen().wait()Popen() 启动外部进程并立即返回,无需等待它退出。 .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

Grandchild 的输出被重定向到 /dev/null,即它不会继承父管道,因此 check_output() 可能会在不等待的情况下退出。

注意:&amp; 最后将孙子 python 进程置于后台。它不适用于默认情况下 shell=Truecmd.exe 开头的 Windows。

【讨论】:

太棒了,谢谢@jfs的详细解释

以上是关于Python 子进程 .check_call 与 .check_output的主要内容,如果未能解决你的问题,请参考以下文章

如何从python子进程调用LC_ALL = C排序[重复]

python 怎么启动一个外部命令程序,并且不阻塞当前进程

保存子进程命令的错误信息

Python, subprocess, call(), check_call 和 returncode 来查找命令是不是存在

阻塞和非阻塞子进程调用

以与长时间运行的 Python 进程不同的用户身份运行子进程