Bash中重复重定向至同一文件的异常行为及原理问询
Bash重复重定向至同一文件的异常机制解析
两种重定向的本质差异
foo 1>a.txt 2>&1:先将stdout(文件描述符1)指向a.txt,再把stderr(文件描述符2)复制为stdout的文件描述符副本。此时两个描述符共享同一个文件表项,包括文件指针、打开模式等,所有写入操作都会顺着同一个指针位置追加,不会互相干扰。foo 1>a.txt 2>a.txt:独立两次打开同一文件,为stdout和stderr分别创建完全独立的文件描述符,各自维护自己的文件指针和文件表项,这就是异常的根源。
重定向的内部执行流程
Bash会按从左到右的顺序处理所有重定向操作,结合>的打开模式来分析:
- 处理
1>a.txt时,Bash以O_WRONLY | O_CREAT | O_TRUNC模式打开文件——也就是清空文件原有内容,将stdout绑定到这个文件,此时stdout的文件指针停在文件开头。 - 处理
2>a.txt时,Bash再次以同样的截断模式打开a.txt,这一步会再次清空文件,然后将stderr绑定到这个文件,stderr的文件指针也停在文件开头。
当程序foo运行时,两个独立的文件指针会导致写入冲突:
- 假设程序先向stdout输出内容,这些内容会写到文件中,但stderr的指针仍停在开头;后续如果向stderr输出,会直接从文件开头写入,覆盖掉之前stdout的内容。
- 如果输出交替进行,两个指针各自移动,还可能出现文件空洞(即中间部分是空白),最终文件里只会保留最后写入的部分内容,或者出现混乱的重叠内容。
而1>a.txt 2>&1的流程则完全不同:
- 第一步同样是打开
a.txt并截断,绑定stdout。 - 第二步是复制stdout的文件描述符给stderr,两者共享同一个文件表项和文件指针。不管是stdout还是stderr写入,都会从当前指针位置追加,所有内容会按输出顺序连续保存到文件中。
示例验证
假设foo是如下脚本:
#!/bin/bash echo "stdout1" echo "stderr1" >&2 echo "stdout2"
执行./foo 1>a.txt 2>a.txt后,a.txt的内容大概率是:
stderr1 stdout2
原因是第二次打开a.txt截断了文件,覆盖了stdout1,后续stdout2从stdout指针的位置(此时文件长度是stderr1的长度)追加写入,最终就丢失了stdout1。
而执行./foo 1>a.txt 2>&1,a.txt的内容会是:
stdout1 stderr1 stdout2
所有内容按输出顺序完整保留。
内容的提问来源于stack exchange,提问作者naba
相关产品推荐
相关产品推荐

