脚本 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,还是原样
本人小白,求大神~~
1
ipwx 2020-04-23 10:19:56 +08:00 3
env = os.environ.copy()
env['PYTHONUNBUFFERED'] = '1' subprocess.Popen("python3 ./test1.py", shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) |
2
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) |
3
MikuSama 2020-04-23 10:23:56 +08:00
|
6
sunzy OP @ipwx 恩,我是要执行一个命令(ffmpeg -i %s -f flv %s -hide_banner),使用 list 命令列表总是报错,换成 shell=True 就好了
|
7
ipwx 2020-04-23 10:54:16 +08:00
@sunzy 没啥特别的,Python 有个机制,检测输出设备是啥。如果不是交互式的 terminal,那么就会自动打开 python 自己的输出缓存。这和你调用 python 脚本的输入缓存无关,人家没有把数据推过来,你的调用者怎么做也是枉然。
|
8
ipwx 2020-04-23 10:54:33 +08:00
PYTHONUNBUFFERED=1 这个环境变量能关掉这个机制。
|
9
sunzy OP @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()) ``` |
10
crella 2020-04-23 12:24:48 +08:00 via Android 1
@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 沟通,一直没搞好…… |
11
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,直到程序退出。所以无论什么时候,这两行这么写都是不对的。除非你开两个线程后台读取。 |
12
ipwx 2020-04-23 13:40:54 +08:00
@sunzy 所以用 subprocess.Popen(..., stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 最最方便的选择。
|
13
ipwx 2020-04-23 13:42:30 +08:00
|
14
sunzy OP @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 库了(看了一下,还是要学习成本了),等这个忙完了再看吧 |
16
ipwx 2020-04-23 14:39:30 +08:00
@sunzy 嗷原来你是 windows 啊。我一直在 linux 和 mac 下做,而且从来没用过 readlines,这倒是没注意到。
|
17
Mohanson 2020-04-23 14:40:35 +08:00
py 的 print 函数有缓存的, print(xxx, flush=True) 可以强制 flush
|
18
sunzy OP @ipwx 在 Mac 下开发,在 Linux 服务器上跑。 用 universal_newlines 这个参数是因为 ffmpeg 输出进度的时候用的是"\r"
|
19
sunzy OP @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
|
20
ipwx 2020-04-23 14:45:36 +08:00
@sunzy 嘛嘛。我一般用 .read(n) 直接 stdout.buffer.write 这种,Popen 我还真没用过 readline
|
21
ipwx 2020-04-23 14:48:29 +08:00
我知道了你是不是要处理 ffmpeg 的进度输出。那个没换行,所以要么是 \b 退格,要么是 \r 回到行首。所以没有 \n 你之前用 readline 搞不定很正常。像我一般用 .read 手动处理界定符,真注意不到这个。
|
22
mathzhaoliang 2020-04-23 14:53:48 +08:00
我会这样写
```python process = subprocess.Popen( ffmpeg_command, shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE) _, err = process.communicate() if process.returncode: print(type(err), err) raise IOError("FFMPEG error: " + err.decode("ascii")) ``` |
23
FaiChou 2020-04-23 15:10:51 +08:00
应该是相同的问题,我在 node 中遇到过 https://www.v2ex.com/t/645816#reply2
|
25
sunzy OP @mathzhaoliang 这样写只能获取最后的信息吧
|
26
ipwx 2020-04-23 20:46:52 +08:00
@sunzy 基本思路:
buf = b'' def handle(cnt): ....buf += cnt ....while True: ........pos = buf.find(b'\n‘) ........if pos <= -1: ............break ........handle_line(buf[:pos]) ........buf = buf[pos:] def handle_line(line): ....do what you want to do while not eof: ....handle(read(n)) if buf: ....handle_line(buf) |
27
ipwx 2020-04-23 20:47:30 +08:00
就是用个缓冲区自己解析一下。上面是伪代码,不是真的能运行的代码。
|
29
yyfeng88625 2020-04-24 12:08:35 +08:00 1
使用 sh 库就好,自己写里面很多坑的
https://amoffat.github.io/sh/tutorials/real_time_output.html |
30
sunzy OP @yyfeng88625 这个库不错,学习了,感谢!
|