使用 subprocess 执行另一个脚本,如何实时输出?

2020-04-23 10:18:10 +08:00
 sunzy

脚本 1,负责输出


for i in range(1, 10):
    print(i)
    time.sleep(1)

脚本 2,用 subprocess 执行脚本 1,获取 stdout 并输出

import subprocess

proc = subprocess.Popen("python3 ./test1.py", shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while True:
  line = proc.stdout.readline()
  if not line:
    break
  print(line.rstrip())

期望的是实时输出,结果是一直等到脚本 1 执行完了才整体输出所有的内容

stackoverflow 上有一篇Non-blocking read on a subprocess.PIPE in python ,我试了线程和 fcntl,还是原样

本人小白,求大神~~

4999 次点击
所在节点    Python
30 条回复
ipwx
2020-04-23 10:19:56 +08:00
env = os.environ.copy()
env['PYTHONUNBUFFERED'] = '1'
subprocess.Popen("python3 ./test1.py", shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
ipwx
2020-04-23 10:21:34 +08:00
emmmm 重点是 env=env

另外如果是我的话,不会使用 shell=True,也不会设置 bufsize 。我的话会这样:

env = os.environ.copy()
env['PYTHONUNBUFFERED'] = '1'
subprocess.Popen([sys.executable, ...], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
MikuSama
2020-04-23 10:23:56 +08:00
p = subprocess.call("python3 ./test1.py", shell=True)
p.wait()

建议:
import test1
MikuSama
2020-04-23 10:28:32 +08:00
@MikuSama call 换成 Popen= =
sunzy
2020-04-23 10:31:26 +08:00
@ipwx 大神太厉害了!在网上翻遍了资料都不行(哭~)。求问这是什么原因?
sunzy
2020-04-23 10:33:58 +08:00
@ipwx 恩,我是要执行一个命令(ffmpeg -i %s -f flv %s -hide_banner),使用 list 命令列表总是报错,换成 shell=True 就好了
ipwx
2020-04-23 10:54:16 +08:00
@sunzy 没啥特别的,Python 有个机制,检测输出设备是啥。如果不是交互式的 terminal,那么就会自动打开 python 自己的输出缓存。这和你调用 python 脚本的输入缓存无关,人家没有把数据推过来,你的调用者怎么做也是枉然。
ipwx
2020-04-23 10:54:33 +08:00
PYTHONUNBUFFERED=1 这个环境变量能关掉这个机制。
sunzy
2020-04-23 11:13:15 +08:00
@ipwx 这个测试脚本跑通了,但是换成 ffpmeg 就不行了,汗~

```python
env = os.environ.copy()
env['PYTHONUNBUFFERED'] = '1'

r = subprocess.Popen("ffmpeg -i %s -f flv %s -hide_banner" % (source, target), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
while True:
line = r.stdout.readline()
line2 = r.stderr.readline()
if not line and not line2 and r.poll() != None:
break
print(line.rstrip())
print(line2.rstrip())
```
crella
2020-04-23 12:24:48 +08:00
@sunzy 跨语言访问终端输出流很容易出现意料之外的结果。

建议 ffmpeg 结合平台,把 stdout 指向文件 A,然后 python 定时读取文件 A 的最后一行。

windows 上的 cmd 有 >file.txt 输出 stdout 和 2>file.txt 输出 stderr 。ffmpeg 有些信息从 stdout 出,有些信息从 stderr 出。


首先我用 c#读写 ruby 的 stdout 折腾了好久;然后 c#与 ruby 通过 win socket 沟通,一直没搞好……
ipwx
2020-04-23 13:40:17 +08:00
@sunzy stderr=subprocess.STDOUT

你这最大的问题就是这两句:

line = r.stdout.readline()
line2 = r.stderr.readline()

想象一下,如果被调用的程序只通过一个 stdout 输出,那么你在第二行就会一直 hang,直到程序退出。反过来,如果被调用的程序只通过 stderr 输出,那你的程序就会在第一行 hang,直到程序退出。所以无论什么时候,这两行这么写都是不对的。除非你开两个线程后台读取。
ipwx
2020-04-23 13:40:54 +08:00
@sunzy 所以用 subprocess.Popen(..., stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 最最方便的选择。
ipwx
2020-04-23 13:42:30 +08:00
另外 ffmpeg 的话,你可以考虑用库,比如:

https://github.com/kkroening/ffmpeg-python
sunzy
2020-04-23 14:38:06 +08:00
@ipwx 多谢!

找到了 universal_newlines 这个参数,可以完全满足我的需求!

r = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env,universal_newlines=True)
while True:
line = r.stdout.readline()
if not line and r.poll() != None:
break
print(line.rstrip())

先不折腾 ffmpeg-python 库了(看了一下,还是要学习成本了),等这个忙完了再看吧
sunzy
2020-04-23 14:39:00 +08:00
@crella 多谢提醒!这个建议也很不错!
ipwx
2020-04-23 14:39:30 +08:00
@sunzy 嗷原来你是 windows 啊。我一直在 linux 和 mac 下做,而且从来没用过 readlines,这倒是没注意到。
Mohanson
2020-04-23 14:40:35 +08:00
py 的 print 函数有缓存的, print(xxx, flush=True) 可以强制 flush
sunzy
2020-04-23 14:42:22 +08:00
@ipwx 在 Mac 下开发,在 Linux 服务器上跑。 用 universal_newlines 这个参数是因为 ffmpeg 输出进度的时候用的是"\r"
sunzy
2020-04-23 14:43:44 +08:00
@ipwx "The trick is to add universal_newlines=True to the subprocess.Popen() call, because ffmpeg's output is in fact unbuffered but comes with newline-characters" ---stackoverflow
ipwx
2020-04-23 14:45:36 +08:00
@sunzy 嘛嘛。我一般用 .read(n) 直接 stdout.buffer.write 这种,Popen 我还真没用过 readline

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/665236

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX