为什么无缓冲的 stderr 会比有缓冲的 stdout 输出的还晚? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
OhYee
V2EX    问与答

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

  OhYee 2019-01-15 13:49:30 +08:00 2784 次点击
这是一个创建于 2537 天前的主题,其中的信息可能已经有所发展或是发生改变。
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  
   2019-01-15 14:16:19 +08:00   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  
   2019-01-15 14:27:54 +08:00   1
再补充下, 行缓冲是关键, 如果你在 sys.stderr.write 里加上\n, 那么你的输出就直接是符合预期的
OhYee
    3
OhYee  
OP
   2019-01-15 14:29:00 +08:00
@kkk330

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

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

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

```

stderr 明明没有用缓冲区直接输出,竟然反而比走缓冲区的 stdout 还慢。
OhYee
    4
OhYee  
OP
   2019-01-15 14:29:58 +08:00
@kkk330
行缓冲应该不会影响结果吧,就算是 print 的换行影响了结果,stderr 也应该在前面啊
OhYee
    5
OhYee  
OP
   2019-01-15 14:35:58 +08:00
把 print 换成 sys.stdout.write 符合预期了,所以说 print 不是一般意义的 stdout 么
kkk330
    6
kkk330  
   2019-01-15 14:38:06 +08:00   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  
   2019-01-15 14:39:23 +08:00
print 自带\n
也就是 sys.stdout.write("xxxxx\n")等效于 print("xxxxx")
OhYee
    8
OhYee  
OP
   2019-01-15 14:40:54 +08:00
stderr 不是无缓冲的么?他不应该直接输出不用等\n 么
OhYee
    9
OhYee  
OP
   2019-01-15 14:41:13 +08:00
@kkk330 stderr 不是无缓冲的么?他不应该直接输出不用等\n 么
kkk330
    10
kkk330  
   2019-01-15 14:45:56 +08:00   1
"When interactive, stdout and stderr streams are line-buffered."

你应该是直接在终端里执行的命令, 因为是 tty, 所以属于交互式的, 所以是行缓冲
kkk330
    11
kkk330  
   2019-01-15 14:46:33 +08:00
我前面还故意列了几个执行方式让你看看他们的表现的...
swulling
    12
swulling  
   2019-01-15 14:47:30 +08:00   1
@OhYee stderr 在 Python3 是 line-buffered 的,在 Python2 是无 buffer 的
从 print()看,你这个是 Python3 吧,一楼说的很清楚了
OhYee
    13
OhYee  
OP
   2019-01-15 14:51:05 +08:00
@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
   2019-01-15 14:53:26 +08:00
@swulling
python3 里不也是 stdout 是 line buffer,stderr 是无 buffer 么

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

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

另外 13 楼的输出被截断也是 buffer 的问题么?
OhYee
    15
OhYee  
OP
   2019-01-15 15:04:45 +08:00
@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  
   2019-01-15 15:09:26 +08:00
"你应该是直接在终端里执行的命令, 因为是 tty, 所以属于交互式的, 所以是行缓冲"

你在 15 楼贴的就是"在终端里执行的命令"
whileFalse
    17
whileFalse  
   2019-01-15 15:12:16 +08:00
@kkk330 学习了 谢谢
OhYee
    18
OhYee  
OP
   2019-01-15 15:19:19 +08:00
@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  
   2019-01-15 15:42:57 +08:00   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
   2019-01-15 15:52:08 +08:00
@kkk330 @swulling

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

另外关于-u 会截断输出内容的方面有什么解释么?
swulling
    21
swulling  
   2019-01-15 16:01:38 +08:00   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
   2019-01-15 16:20:28 +08:00
@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.
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2580 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 24ms UTC 05:15 PVG 13:15 LAX 21:15 JFK 00:15
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