使用 subprocess 执行另一个脚本,如何实时输出? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
sunzy
V2EX    Python

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

  •  
  •   sunzy 2020-04-23 10:18:10 +08:00 5364 次点击
    这是一个创建于 2003 天前的主题,其中的信息可能已经有所发展或是发生改变。

    脚本 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,还是原样

    本人小白,求大神~~

    30 条回复    2020-05-03 22:20:28 +08:00
    ipwx
        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)
    ipwx
        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)
    MikuSama
        3
    MikuSama  
       2020-04-23 10:23:56 +08:00
    p = subprocess.call("python3 ./test1.py", shell=True)
    p.wait()

    建议:
    import test1
    MikuSama
        4
    MikuSama  
       2020-04-23 10:28:32 +08:00
    @MikuSama call 换成 Popen= =
    sunzy
        5
    sunzy  
    OP
       2020-04-23 10:31:26 +08:00
    @ipwx 大神太厉害了!在网上翻遍了资料都不行(哭~)。求问这是什么原因?
    sunzy
        6
    sunzy  
    OP
       2020-04-23 10:33:58 +08:00
    @ipwx 恩,我是要执行一个命令(ffmpeg -i %s -f flv %s -hide_banner),使用 list 命令列表总是报错,换成 shell=True 就好了
    ipwx
        7
    ipwx  
       2020-04-23 10:54:16 +08:00
    @sunzy 没啥特别的,Python 有个机制,检测输出设备是啥。如果不是交互式的 terminal,那么就会自动打开 python 自己的输出缓存。这和你调用 python 脚本的输入缓存无关,人家没有把数据推过来,你的调用者怎么做也是枉然。
    ipwx
        8
    ipwx  
       2020-04-23 10:54:33 +08:00
    PYTHOnUNBUFFERED=1 这个环境变量能关掉这个机制。
    sunzy
        9
    sunzy  
    OP
       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
        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 沟通,一直没搞好……
    ipwx
        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,直到程序退出。所以无论什么时候,这两行这么写都是不对的。除非你开两个线程后台读取。
    ipwx
        12
    ipwx  
       2020-04-23 13:40:54 +08:00
    @sunzy 所以用 subprocess.Popen(..., stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 最最方便的选择。
    ipwx
        13
    ipwx  
       2020-04-23 13:42:30 +08:00
    另外 ffmpeg 的话,你可以考虑用库,比如:

    https://github.com/kkroening/ffmpeg-python
    sunzy
        14
    sunzy  
    OP
       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
        15
    sunzy  
    OP
       2020-04-23 14:39:00 +08:00
    @crella 多谢提醒!这个建议也很不错!
    ipwx
        16
    ipwx  
       2020-04-23 14:39:30 +08:00
    @sunzy 嗷原来你是 windows 啊。我一直在 linux 和 mac 下做,而且从来没用过 readlines,这倒是没注意到。
    Mohanson
        17
    Mohanson  
       2020-04-23 14:40:35 +08:00
    py 的 print 函数有缓存的, print(xxx, flush=True) 可以强制 flush
    sunzy
        18
    sunzy  
    OP
       2020-04-23 14:42:22 +08:00
    @ipwx 在 Mac 下开发,在 Linux 服务器上跑。 用 universal_newlines 这个参数是因为 ffmpeg 输出进度的时候用的是"\r"
    sunzy
        19
    sunzy  
    OP
       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
        20
    ipwx  
       2020-04-23 14:45:36 +08:00
    @sunzy 嘛嘛。我一般用 .read(n) 直接 stdout.buffer.write 这种,Popen 我还真没用过 readline
    ipwx
        21
    ipwx  
       2020-04-23 14:48:29 +08:00
    我知道了你是不是要处理 ffmpeg 的进度输出。那个没换行,所以要么是 \b 退格,要么是 \r 回到行首。所以没有 \n 你之前用 readline 搞不定很正常。像我一般用 .read 手动处理界定符,真注意不到这个。
    mathzhaoliang
        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"))
    ```
    ethusdt
        23
    ethusdt  
       2020-04-23 15:10:51 +08:00
    应该是相同的问题,我在 node 中遇到过 t/645816#reply2
    sunzy
        24
    sunzy  
    OP
       2020-04-23 15:20:14 +08:00
    @ipwx 是的,是要监控进度输出,用.read(n)怎么读取正好一行的数据呢?
    sunzy
        25
    sunzy  
    OP
       2020-04-23 15:32:01 +08:00
    @mathzhaoliang 这样写只能获取最后的信息吧
    ipwx
        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)
    ipwx
        27
    ipwx  
       2020-04-23 20:47:30 +08:00
    就是用个缓冲区自己解析一下。上面是伪代码,不是真的能运行的代码。
    sunzy
        28
    sunzy  
    OP
       2020-04-24 09:45:42 +08:00
    @ipwx 赞!
    yyfeng88625
        29
    yyfeng88625  
       2020-04-24 12:08:35 +08:00   1
    使用 sh 库就好,自己写里面很多坑的
    https://amoffat.github.io/sh/tutorials/real_time_output.html
    sunzy
        30
    sunzy  
    OP
       2020-05-03 22:20:28 +08:00
    @yyfeng88625 这个库不错,学习了,感谢!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2655 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 01:17 PVG 09:17 LAX 18:17 JFK 21:17
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86