我觉得出现 a 未被清空的情况不算 bug,而且应该与版本无关。
对 cat a | tr -d b > a 运行一万次结果如下,少数情况下 cat 读到的 a 是没被清空的。
代码: 全选
for x in {0..10000} ; do echo abc > a ; cat a | tr -d b > a ; cat a ; done
ac
ac
ac
ac
ac
ac
ac
ac
ac
ac
ac
ac
ac
用 strace 跟踪系统调用:
代码: 全选
$ echo abc > a
$ strace -f -o test.log bash -c 'cat a | tr -d b > a'
$ cat test.log | grep -e clone -e pipe -e dup2 -e close -e open -e exec
15506 是父进程 PID,15507、15508 是两个子进程 PID。尽管用 grep 进行了过滤,仍有一些无关信息,下面略去了这些信息。
15506 pipe([3, 4]) = 0
# 父进程用 pipe() 打开管道,相应文件描述符是 3 和 4,其中 3 是读端,4 是写端 。
15506 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD
, child_tidptr=0xb762d728) = 15507
15506 close(4) = 0
15506 close(4) = -1 EBADF (Bad file descriptor)
# 父进程用 clone() 产生一个子进程,然后关闭管道写端 4 。
15506 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD
, child_tidptr=0xb762d728) = 15508
15506 close(3) = 0
# 父进程用 clone() 产生一个子进程,然后关闭管道读端 3。
# 这时子进程 15508 是只有管道的读端的,因为写端在 clone() 之前就关闭了。
15507 close(3 <unfinished ...>
15507 <... close resumed> ) = 0
15507 dup2(4, 1 <unfinished ...>
# 子进程 15507 将管道写端 4 用 dup2() 赋给标准输出。
15507 <... dup2 resumed> ) = 1
15507 close(4 <unfinished ...>
15507 <... close resumed> ) = 0
15508 dup2(3, 0 <unfinished ...>
# 子进程 15508 将管道读端 3 用 dup2() 赋给标准输入。
15508 <... dup2 resumed> ) = 0
15508 close(3 <unfinished ...>
15508 <... close resumed> ) = 0
15508 open("a", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
15508 dup2(3, 1) = 1
# 子进程 15508 打开文件“a”,并赋给标准输出。因为有 O_TRUNC,文件“a”被清空。即对应于重定向“ > a”。
15508 close(3) = 0
15508 execve("/usr/bin/tr", ["tr", "-d", "b"], [/* 39 vars */]) = 0
# 子进程 15508 用 execve() 执行 "tr -d b "。
15507 execve("/bin/cat", ["cat", "a"], [/* 39 vars */]) = 0
# 子进程 15507 执行 "cat a"。
15507 open("a", O_RDONLY|O_LARGEFILE) = 3
15507 close(3) = 0
# 子进程 15507 打开文件“a”,相当于 cat 读取 a。
是 cat 先读了 a,还是重定向先清空了 a 取决于哪一个先被执行。而从 clone() 开始两个子进程是完全独立的,重定向和 exec() 又都是在 clone() 后才进行的,因而执行先后不是绝对的,受进程调度等影响,两种结果都可能出现。
系统 Debian GNU/Linux Wheezy i386
代码: 全选
$ bash --version
GNU bash, version 4.2.37(1)-release (i486-pc-linux-gnu)
...
$ cat --version
cat (GNU coreutils) 8.13
...
更正:系统 amd64 -> i386