【问题标题】:Poll subprocess finished while looping stdout循环标准输出时轮询子进程完成
【发布时间】:2021-01-11 09:37:10
【问题描述】:

我正在编写一个脚本,它以不可预测的大小生成输出,我想从循环内部知道脚本何时完成。

这是代码:

#!/usr/bin/env python3
import subprocess
import shlex

def main():
    cmd = 'bash -c "for i in $(seq 1 15);do echo $i ;sleep 1;done"'
    print(cmd)
    p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE,
                         universal_newlines=True)
    for line in p.stdout:
        print(f"file_name: {line.strip()}")
        print(p.poll())

if __name__ == "__main__":
    main()

即使在最后一次迭代中,p.poll() 也始终是 None,这是有道理的,因为在 echo 之后它 sleeps 持续 1 秒钟,然后再进行下一次迭代并完成。

有什么办法让它工作吗?

【问题讨论】:

  • 循环之后:p.wait()print(p.poll()) 应该可以解决问题。
  • @zvone 当然可以,但我想看看我是否可以从循环内部做到这一点。

标签: python python-3.x subprocess


【解决方案1】:

您已经发现了问题所在,即子进程输出最后一行后,它仍会继续运行一秒钟,因此当程序处于循环中时,将始终可以看到该程序正在运行。即使您将调用移至循环外的poll,您也可能需要稍等片刻才能让子进程在输出其最终消息后有机会终止(我已经减小了循环大小——生命太短了):

#!/usr/bin/env python3
import subprocess
import shlex
import time

def main():
    cmd = 'bash -c "for i in $(seq 1 5);do echo $i; sleep 1; done;"'
    print(cmd)
    p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, universal_newlines=True)
    for line in p.stdout:
        print(f"file_name: {line.strip()}", flush=True)
    print(p.poll())
    time.sleep(.1)
    print(p.poll())

if __name__ == "__main__":
    main()

打印:

bash -c "for i in $(seq 1 5);do echo $i; sleep 1; done;"
file_name: 1
file_name: 2
file_name: 3
file_name: 4
file_name: 5
None
0

要在循环内“让它工作”,需要对子流程内发生的事情有特殊的了解。根据前面的代码,我们需要:

#!/usr/bin/env python3
import subprocess
import shlex
import time

def main():
    cmd = 'bash -c "for i in $(seq 1 5);do echo $i; sleep 1; done;"'
    print(cmd)
    p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, universal_newlines=True)
    for line in p.stdout:
        # has to be greater than the sleep time in the subprocess to give the subprocess a chance to terminate
        print(f"file_name: {line.strip()}", flush=True)
        time.sleep(1.1)
        print(p.poll())

if __name__ == "__main__":
    main()

打印:

bash -c "for i in $(seq 1 5);do echo $i; sleep 1; done;"
file_name: 1
None
file_name: 2
None
file_name: 3
None
file_name: 4
None
file_name: 5
0

但这几乎不是一个实用的解决方案。人们不得不问进行这项民意调查的原因是什么?它没有提供任何有用的信息,除非您愿意在读取之后包含 sleep 调用因为在子进程完成最后一次写入及其终止之后总会有一些延迟,并且这些 sleep 调用一般都是浪费。您应该一直阅读,直到没有更多输出,然后执行 p.wait() 以等待子进程终止,但这是您的选择:

#!/usr/bin/env python3
import subprocess
import shlex

def main():
    cmd = 'bash -c "for i in $(seq 1 5);do echo $i; sleep 1; done;"'
    print(cmd)
    p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, universal_newlines=True)
    for line in p.stdout:
        print(f"file_name: {line.strip()}", flush=True)
    p.wait()

if __name__ == "__main__":
    main()

【讨论】:

  • 据我所知,我的情况很复杂,我会接受答案。感谢您的详细解释。
猜你喜欢
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 2011-03-08
  • 2023-04-10
  • 2011-08-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多