docker exec在命令替换中的输出异常及重定向疑问咨询
执行的命令与输出
- 命令1:
docker exec -it stupefied_poitras bash -c 'time date'
输出:Thu Sep 19 05:02:04 UTC 2024 real 0m0.000s user 0m0.000s sys 0m0.000s - 命令2:
A=$(docker exec -it stupefied_poitras bash -c 'time date') && echo $A
输出:sys 0m0.000ss02:09 UTC 2024 - 命令3:
A=$(docker exec -it stupefied_poitras bash -c 'time date' 1>&2) && echo $A
输出:Thu Sep 19 05:14:59 UTC 2024 real 0m0.002s user 0m0.001s sys 0m0.000s
补充说明
执行A=$(docker exec -it bash bash -c 'time date' 1>&2) && echo '---' && echo $A时,输出如下:
Thu Sep 19 05:30:32 UTC 2024 real 0m0.000s user 0m0.000s sys 0m0.000s ---
此时变量A为空,说明第二组命令的控制台输出实际是stderr内容,而非变量A的内容。
疑问
- 第二组命令中为何仅显示stderr的最后一行,且出现内容覆盖(疑似回车导致)?
- 该异常仅在使用docker exec时出现,直接执行
A=$(bash -c 'time date') && echo $A时time输出在date之前,为何顺序相反? - 若将stderr重定向到stdout(2>&1),会出现和第二组相同的混乱输出,为何重定向方向会影响结果?
问题解答
1. 第二组命令输出混乱的原因
bash的time内置命令默认把计时信息输出到stderr,date的输出则是stdout。命令替换$(...)只会捕获stdout内容,stderr会直接打印到终端。
使用docker exec -it时,-it参数会分配伪终端(PTY)。PTY环境下,stdout是行缓冲模式,stderr是无缓冲模式,两者共用同一个输出流。date的stdout内容被命令替换捕获,不会直接输出,但PTY已经为其预留了输出位置;而time的stderr内容无缓冲直接输出,就会和预留位置的内容重叠,最终呈现出time最后一行覆盖部分date内容的混乱结果。
2. Docker exec与直接执行的顺序差异
直接执行A=$(bash -c 'time date') && echo $A时,没有PTY,stdout是块缓冲(非交互模式),stderr是无缓冲。time的stderr内容无缓冲先输出到终端,date的stdout内容被缓冲后捕获到变量A中,echo $A时才打印出来,所以看起来time输出在date之前。
而用docker exec -it时,PTY的行缓冲特性改变了输出同步性,加上命令替换只捕获stdout,date内容被捕获不输出,time的stderr直接输出并引发覆盖,最终呈现出和直接执行完全不同的顺序和混乱现象。
3. 重定向方向影响结果的原因
- 执行
2>&1时,stderr被重定向到stdout,time的计时信息和date的输出都进入stdout,被命令替换捕获。但PTY下两者缓冲方式不同,输出字节流交错,再加上echo $A未加引号会压缩所有换行和空白,最终导致内容混乱重叠,和第二组现象一致。 - 执行
1>&2时,stdout被重定向到stderr,date和time的输出都直接到终端(stderr不被命令替换捕获),两者在PTY中按正常顺序输出,没有被捕获或压缩,所以显示正常,且变量A为空(无stdout内容被捕获)。
内容的提问来源于stack exchange,提问作者user1335014

