为什么无缓冲的 stderr 会比有缓冲的 stdout 输出的还晚? - V2EX
OhYee

为什么无缓冲的 stderr 会比有缓冲的 stdout 输出的还晚?

  •  
  •   OhYee Jan 15, 2019 3027 views
    This topic created in 2678 days ago, the information mentioned may be changed or developed.
    import sys sys.stdout.write("stdout1 ") sys.stderr.write("stderr1 ") sys.stdout.write("stdout2 ") sys.stderr.write("stderr2 ") print() string = "" for i in range(10): string = string + str(i) + "\n" print(string) 

    在使用 python test.py 执行时,应该是有开启缓冲区的,不加上后面的 print 部分,输出顺序是 stderr1 stderr2 stdout1 stdout2

    但是为什么加了后面的 print 部分输出变成了

    stdout1 stdout2 0 1 2 3 4 5 6 7 8 9 stderr1 stderr2 

    stderr 不是无缓冲的么,不应该直接就输出出来在最前面么?

    kkk330
        1
    kkk330  
       Jan 15, 2019   1
    https://docs.python.org/3/library/sys.html#sys.stderr

    里面有提到
    When interactive, stdout and stderr streams are line-buffered. Otherwise, they are block-buffered like regular text files. You can override this value with the -u command-line option.

    ```shell

    python3 test.py

    python3 test.py &> buffer.log

    python3 -u test.py

    ```

    顺便, 也可以看看 python2 的表现, 我这边测试了下是有差异的
    kkk330
        2
    kkk330  
       Jan 15, 2019   1
    再补充下, 行缓冲是关键, 如果你在 sys.stderr.write 里加上\n, 那么你的输出就直接是符合预期的
    OhYee
        3
    OhYee  
    OP
       Jan 15, 2019
    @kkk330

    我确认了下我是有开缓冲区的,没有加-u,PYTHONUNBUFFERED 也是空的

    如果按照缓冲区的理解,输出结果应该是这样才对

    ```python
    stderr1 stderr2 stdout1 stdout2
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

    ```

    stderr 明明没有用缓冲区直接输出,竟然反而比走缓冲区的 stdout 还慢。
    OhYee
        4
    OhYee  
    OP
       Jan 15, 2019
    @kkk330
    行缓冲应该不会影响结果吧,就算是 print 的换行影响了结果,stderr 也应该在前面啊
    OhYee
        5
    OhYee  
    OP
       Jan 15, 2019
    把 print 换成 sys.stdout.write 符合预期了,所以说 print 不是一般意义的 stdout 么
    kkk330
        6
    kkk330  
       Jan 15, 2019   1
    是行缓冲的原因, 可以一步一步看
    sys.stdout.write("stdout1 ") # stdout 里变成了"stdout1 "
    sys.stderr.write("stderr1 ") # stderr 里变成了"stderr1 "
    sys.stdout.write("stdout2 ") # stdout 里变成了"stdout1 stdout2 "
    sys.stderr.write("stderr2 ") # stderr 里变成了"stderr1 stderr2"

    print() # stdout 里变成了"stdout1 stdout2 \n" 因为是行缓冲, 发现了\n, 所以就 flush 到 tty 了

    .....

    # 代码最后这里执行完了, stderr 仍然没有\n, 因为程序结束了, 还是会被 flush 出来, 所以 stderr 在最后输出
    kkk330
        7
    kkk330  
       Jan 15, 2019
    print 自带\n
    也就是 sys.stdout.write("xxxxx\n")等效于 print("xxxxx")
    OhYee
        8
    OhYee  
    OP
       Jan 15, 2019
    stderr 不是无缓冲的么?他不应该直接输出不用等\n 么
    OhYee
        9
    OhYee  
    OP
       Jan 15, 2019
    @kkk330 stderr 不是无缓冲的么?他不应该直接输出不用等\n 么
    kkk330
        10
    kkk330  
       Jan 15, 2019   1
    "When interactive, stdout and stderr streams are line-buffered."

    你应该是直接在终端里执行的命令, 因为是 tty, 所以属于交互式的, 所以是行缓冲
    kkk330
        11
    kkk330  
       Jan 15, 2019
    我前面还故意列了几个执行方式让你看看他们的表现的...
    swulling
        12
    swulling  
       Jan 15, 2019   1
    @OhYee stderr 在 Python3 是 line-buffered 的,在 Python2 是无 buffer 的
    从 print()看,你这个是 Python3 吧,一楼说的很清楚了
    OhYee
        13
    OhYee  
    OP
       Jan 15, 2019
    @kkk330
    python test.py 运行的
    不是直接终端运行的。

    别的地方都和我理解的一样,只有混着 print 不符合理解
    感觉 stderr 的无缓冲也不是简单的无缓冲

    比如针对
    '''python
    string = ""
    for i in range(10000):
    string = string + str(i) + "\n"
    '''

    因为在不加-u 时,
    sys.stderr.write(string)
    sys.stdout.write(string)
    可以正常输出(输出到 9999)

    而加上-u,
    sys.stderr.write(string)
    sys.stdout.write(string)
    两者都只能输出到 2638
    OhYee
        14
    OhYee  
    OP
       Jan 15, 2019
    @swulling
    python3 里不也是 stdout 是 line buffer,stderr 是无 buffer 么

    所以按照期望不管后面的内容有没有换行, stderr1 应该一定在 stdout2 前面吧

    按照我测试的情况来看,stderr 也是 line-buffer,不过网上貌似都是说 stderr 是没有 buffer 的。

    另外 13 楼的输出被截断也是 buffer 的问题么?
    OhYee
        15
    OhYee  
    OP
       Jan 15, 2019
    @kkk330

    几种执行方式的结果来看

    (因为被认为是外链,所以. 后面都加了空格)

    python3 test. py 不符合预期,stderr 作为无 buffer 不应该在最后么?
    python3 -u test. py 符合预期
    python3 test. py >log. txt 符合期
    python3 test. py &>log. txt 符合预期



    执行结果:
    ```
    dev@u:~$ python3 test. py
    stdout1 stdout2
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

    stderr1 stderr2 dev@u:~$
    dev@u:~$
    dev@u:~$ python3 -u test. py
    stdout1 stdout2
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

    stderr1 stderr2 dev@u:~$
    dev@u:~$
    dev@u:~$ python3 test. py >log. txt
    stderr1 stderr2 dev@u:~$
    dev@u:~$ cat log. txt
    stdout1 stdout2
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

    dev@u:~$ python3 test. py &>log. txt
    dev@u:~$ cat log. txt
    stderr1 stderr2 stdout1 stdout2
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ```
    kkk330
        16
    kkk330  
       Jan 15, 2019
    "你应该是直接在终端里执行的命令, 因为是 tty, 所以属于交互式的, 所以是行缓冲"

    你在 15 楼贴的就是"在终端里执行的命令"
    whileFalse
        17
    whileFalse  
       Jan 15, 2019
    @kkk330 学习了 谢谢
    OhYee
        18
    OhYee  
    OP
       Jan 15, 2019
    @kkk330

    Interactive interpreter 不是指直接 python3 然后终端里写代码么
    类似
    $ python3
    Python 3.6.7 (default, Oct 22 2018, 11:32:17)
    [GCC 8.2.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>>

    https://docs.python.org/3/tutorial/interpreter.html

    这种应该是属于 script 运行吧

    另外,看了下文档,block-buffered 要怎么翻译啊, 块缓冲还是禁用缓冲?

    我没有在官方文档找到百度上常见的 stderr 是无缓冲类似的解释
    swulling
        19
    swulling  
       Jan 15, 2019   1
    @OhYee https://docs.python.org/3/library/sys.html#sys.stderr

    When interactive, stdout and stderr streams are line-buffered. Otherwise, they are block-buffered like regular text files. You can override this value with the -u command-line option.

    你不懂英文么?
    OhYee
        20
    OhYee  
    OP
       Jan 15, 2019
    @kkk330 @swulling

    OK, 明白了, python 里 stderr 是 line-buffer。
    查到的资料有点混乱,而且 py2 的解释比较多所以搞乱了。
    感谢两位大佬的解释。

    另外关于-u 会截断输出内容的方面有什么解释么?
    swulling
        21
    swulling  
       Jan 15, 2019   1
    @OhYee -u 要看哪个版本,如果是 Python 3 但是是 3.7 以下,那么 -u 也不是 unbuffered,还是 line-buffered
    如果是 3.7+,那么
    Changed in version 3.7: The text layer of the stdout and stderr streams now is unbuffered.
    OhYee
        22
    OhYee  
    OP
       Jan 15, 2019
    @swulling
    感谢,试了下 python3.6.7 没有问题,python3.7.1 存在这个问题
    看了下两者的区别,应该就是 unbuffered 导致被截断掉了

    此贴完结,感谢几位大佬的帮助



    Python3.6

    https://docs.python.org/3.6/using/cmdline.html#cmdoption-u
    -u
    Force the binary layer of the stdout and stderr streams (which is available as their buffer attribute) to be unbuffered. The text I/O layer will still be line-buffered if writing to the console, or block-buffered if redirected to a non-interactive file.

    See also PYTHONUNBUFFERED.

    https://docs.python.org/3.6/using/cmdline.html#envvar-PYTHONUNBUFFERED
    PYTHONUNBUFFERED
    If this is set to a non-empty string it is equivalent to specifying the -u option.



    Python3.7

    https://docs.python.org/3.7/using/cmdline.html#cmdoption-u
    -u
    Force the stdout and stderr streams to be unbuffered. This option has no effect on the stdin stream.

    See also PYTHONUNBUFFERED.

    Changed in version 3.7: The text layer of the stdout and stderr streams now is unbuffered.

    https://docs.python.org/3.7/using/cmdline.html#envvar-PYTHONUNBUFFERED
    PYTHONUNBUFFERED
    If this is set to a non-empty string it is equivalent to specifying the -u option.
    About     Help     Advertise     Blog     API     FAQ     Solana     887 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 48ms UTC 20:26 PVG 04:26 LAX 13:26 JFK 16:26
    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