【问题标题】:python subprocess won't interleave stderr and stdout as what terminal doespython子进程不会像终端那样交错stderr和stdout
【发布时间】:2020-05-31 20:30:49
【问题描述】:

一个测试程序

#!/usr/bin/env python3

import sys

count = 0
sys.stderr.write('stderr, order %d\n' % count)
count += 1
sys.stdout.write('stdout, order %d\n' % count)
count += 1
sys.stderr.write('stderr, order %d\n' % count)
count += 1
sys.stdout.write('stdout, order %d\n' % count)

当通过终端调用时,预期的输出是,

stderr, order 0
stdout, order 1
stderr, order 2
stdout, order 3

在交互式 shell 中,当我将 stdout 重定向到 PIPE 时,输出顺序与上面的输出顺序不同,其中 Popen 会将 stderr 分组并将它们全部写入,然后对 stdout 执行相同的操作,而不是交错 stdout 和 stderr。

In [29]: a = sp.run(['./test.py'], stderr=sp.STDOUT)
stderr, order 0
stdout, order 1
stderr, order 2
stdout, order 3

In [30]: a
Out[30]: CompletedProcess(args=['./test.py'], returncode=0)

In [33]: b = sp.Popen(['./test.py'], stderr=sp.STDOUT, stdout=sp.PIPE, encoding='utf-8')

In [34]: print(b.communicate()[0])
stderr, order 0
stderr, order 2
stdout, order 1
stdout, order 3

【问题讨论】:

    标签: python python-3.x subprocess


    【解决方案1】:

    在 C 库(以及基于 c 的 python)中,流的处理方式不同,具体取决于它们是否附加到交互式终端(或假装的终端)。对于tty,stdout 是行缓冲的,否则它的块是缓冲的,并且仅在遇到某个块边界时才刷新到文件描述符。当您重定向到 PIPE 时,流不再是 tty,并且块缓冲生效。

    解决方案是重新打开stdout,指定无论如何都需要行缓冲 (1)。在 C 级别,stderr 始终是行缓冲的,但是当我测试只是重新打开 stdout 时,程序的行为就好像 stderr 是块缓冲的。我很惊讶。也许这是中间io.TextIO 层或其他一些奇怪的东西,但我发现我需要修复两个管道。

    尽管stdout 和stderr 进入同一个管道,但就执行的程序而言,它们是具有单独缓冲区的单独文件描述符。这就是为什么即使在块模式下也不会在输出缓冲区中自然发生交错的原因。

    #!/usr/bin/env python3
    
    import sys
    import os
    
    # reopen stdout line buffered
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
    
    # this surprises me, seems like we have to reopen stderr
    # line buffered, but i thought it was line buffered anywy.
    # perhaps its the intermediate python TextIO layer?
    sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1)
    
    count = 0
    sys.stderr.write('stderr, order %d\n' % count)
    count += 1
    sys.stdout.write('stdout, order %d\n' % count)
    count += 1
    sys.stderr.write('stderr, order %d\n' % count)
    count += 1
    sys.stdout.write('stdout, order %d\n' % count)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-03
      • 2011-07-07
      • 1970-01-01
      • 2023-04-06
      • 2018-03-16
      • 1970-01-01
      • 2020-06-23
      • 1970-01-01
      相关资源
      最近更新 更多