Python 子进程 .check_call 与 .check_output

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36169571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 17:31:00  来源:igfitidea点击:

Python subprocess .check_call vs .check_output

pythonbashsshsubprocess

提问by Kevin S.

My python script (python 3.4.3) calls a bash script via subprocess:

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

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

The bashscriptcontains the following line:

bashscript包含以下行:

ssh -MNf somehost

which opens a shared master connection to some remote host to allow some subsequent operations.

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

When executing the python script, it will prompt for password for the sshline but then it blocks after the password is entered and never returns. When I ctrl-C to terminate the script, I see that the connection was properly established (so sshline was successfully executed).

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

I don't have this blocking problem when using check_callinstead of check_output, but check_calldoes not retrieve stdout. I'd like to understand what exactly is causing the blocking behavior for check_output, probably related to some subtlety with ssh -MNf.

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

回答by jfs

check_call()returns as soon as /bin/shprocess exits without waiting for descendant processes.

check_call()/bin/sh进程退出后立即返回,无需等待后代进程。

check_output()waits until all output is read. If sshinherits the pipe then check_output()will wait until it exits (until it closes its inherited pipe ends).

check_output()等待直到读取所有输出。如果ssh继承了管道,check_output()则将等待它退出(直到它关闭其继承的管道末端)。

check_call()code example:

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

The output is not read; check_call()returns immediately without waiting for the grandchild background python process.

不读取输出;check_call()无需等待孙子后台python进程即可立即返回。

check_call()is just Popen().wait(). Popen()starts the external process and returns immediately without waiting for it to exit. .wait()collects the exit status for the process -- it doesn't wait for other (grandchildren) processes.

check_call()只是Popen().wait(). Popen()启动外部进程并立即返回,无需等待其退出。.wait()收集进程的退出状态——它不等待其他(孙子)进程。

If the output is read (it is redirected and the grandchild python process inherits the stdout pipe):

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

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

then it waits until the background python process that inherited the pipe exits.

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

check_output()calls Popen().communicate(), to get the output. .communicate()calls .wait()internally i.e., check_output()also waits for the shell to exit and check_output()waits for EOF.

check_output()调用Popen().communicate(), 以获取输出。内部.communicate()调用,.wait()即,check_output()也等待外壳退出并check_output()等待 EOF。

If the grandchild doesn't inherit the pipe then check_output()doesn't wait for it:

如果孙子不继承管道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's output is redirected to /dev/nulli.e., it doesn't inherit the parent's pipe and therefore check_output()may exit without waiting for it.

孙子的输出被重定向到/dev/null即,它不继承父的管道,因此check_output()可能不等待它就退出。

Note: &at the end which puts the grandchild python process into background. It won't work on Windows where shell=Truestarts cmd.exeby default.

注意:&最后将孙子 python 进程置于后台。它不适用于默认shell=True启动cmd.exe的Windows 。