我在bash中使用重定向时发现了一种奇怪且完全出乎意料的行为,即使我设法解决它,我也想知道它为什么会发生。
如果我运行此命令:{ echo wtf > /dev/stdout ; } >> wtf.txt
N次,我希望看到填充N“wtf”行。我在文件中找到的是一行。
我认为既然第一个命令是在截断模式下打开/ dev / stdout,那么模式会被第二个文件描述符(wtf.txt)继承,然后被完全擦除,但是我想知道是否你们中的一些人可能会更好地解释它,如果这是正确的行为或错误。
为了清楚起见,我使用的命令是另一个,但使用 echo 示例更容易理解它。原始命令是一个需要输出文件作为参数的命令,因为我想在stdout上输出,所以我将 / dev / stdout 作为参数传递。可以使用命令openssl rand -hex 4 -out /dev/stdout >> wtf.txt
验证相同的行为。
最后,我设法解决了以下列方式将附加操作委托给 tee 的问题:{ echo wtf > /dev/stdout } | tee -a wtf.txt > /dev/null
答案 0 :(得分:1)
您可以使用strace检查会发生什么:
strace -o wtf-trace.txt -ff bash -c '{ (echo wtf) > /dev/stdout; } >> wtf.txt'
在我的情况下,这将生成两个文件,例如wtf-trace.txt.12889
和wtf-trace.txt.12890
。结果是,流程1 >> wtf.txt
:
open("wtf.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
dup2(3, 1) = 1
close(3) = 0
clone(child_stack=0, .................) = 12890
wait4(-1, [{WIFEXITED(s) .............) = 12890
exit_group(0) = ?
第一个进程打开或创建“wtf.txt”以便附加并获取FD 3.之后,它将FD 1与FD 3复制并关闭FD 3.此时它分叉(克隆),等待它退出并且退出。
第二个进程{ echo wtf > /dev/stdout }
通过FD 1(stdout)继承文件,它确实:
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
dup2(3, 1) = 1
close(3) = 0
fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
write(1, "wtf\n", 4) = 4
exit_group(0) = ?
正如您所见,它会打开/dev/stdout
(注意O_TRUNC
)并获取FD 3,dup2以获取FD 3到FD 1,关闭FD 3,检查FD 1并获取大小为0 st_size=0
,写入并退出。
如果你执行| cat >>
,那么第二个进程会将FD 1连接到一个管道,这个管道不可搜索或截断...
注意:我只显示生成的文件的相关行。