我有一个运行另一个命令的脚本,等待它完成,记录stdout和stderr并根据返回代码执行其他操作。这是代码:
p = subprocess.Popen(command, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
o, e = p.communicate()
if p.returncode:
# report error
# do other stuff
我遇到的问题是如果command
需要很长时间才能运行,其他任何操作都不会完成。如果没有错误没有完成,则不会报告可能的错误以及需要发生的其他事情。如果花费太长时间,它基本上不会超过p.communicate()
。有时这个命令可能需要几个小时(甚至更长时间)才能运行,有时可能需要5秒钟。
我错过了什么或做错了什么?
答案 0 :(得分:1)
根据位于here的文档,可以肯定地说您的代码正在等待子流程完成。
如果你需要去做其他事情'等你的时候可以创建一个循环:
while p.poll():
# 'other things'
time.sleep(0.2)
选择一个合理的睡眠时间,以确定您希望python唤醒和检查子流程以及执行其他事情的频率。
答案 1 :(得分:0)
Popen.communicate
在返回任何内容之前等待进程完成。因此,它不适合任何长时间运行的命令;甚至更少,如果子进程可以挂起等待输入,比如提示输入密码。
如果要将命令的输出捕获到变量中,则stderr=subprocess.PIPE, stdout=subprocess.PIPE
仅需 。如果您输出到终端是正常的,那么您可以删除这两个;甚至使用subprocess.call
代替Popen
。另外,如果你没有为子进程提供输入,那么根本不要使用stdin=subprocess.PIPE
,而是直接从null设备引导(在Python 3.3+中你可以使用{{3在Python< 3.3中使用stdin=open(os.devnull, 'rb')
如果你也需要内容,那么你可以自己阅读p.communicate()
和p.stdout
并输出到终端,而不是调用p.stderr
,但它有点复杂,因为很容易使程序死锁 - 虚拟方法会尝试从子进程'stdout
读取,而子进程想要写入stderr
。对于这种情况,有2种补救措施:
您可以使用stdin=subprocess.DEVNULL
轮询stdout
和stderr
以查看先准备就绪并从中读取的内容
或者,如果您不关心stdout
和stderr
合并为一个,
您可以使用select.select
将stderr流重定向到stdout流:stdout=subprocess.PIPE, stderr=subprocess.STDOUT
;现在所有输出都来到p.stdout
,您可以在循环中轻松读取并输出块,而不必担心死锁:
如果stdout,stderr将是 huge ,你也可以将它们假设为Popen
中的文件;比方说,
stdout = open('stdout.txt', 'w+b')
stderr = open('stderr.txt', 'w+b')
p = subprocess.Popen(..., stdout=stdout, stderr=stderr)
while p.poll() is None:
# reading at the end of the file will return an empty string
err = stderr.read()
print(err)
out = stdout.read()
print(out)
# if we met the end of the file, then we can sleep a bit
# here to avoid spending excess CPU cycles just to poll;
# another option would be to use `select`
if not err and not out: # no input, sleep a bit
time.sleep(0.01)