在 bash 中使用重定向时,我发现了一个奇怪且完全出乎意料的行为,即使我设法解决它,我也想知道它为什么会发生。

如果我运行这个命令:{ echo wtf > /dev/stdout ; } >> wtf.txt N 次,我希望看到填充了 N 个“wtf”的行。我在文件中发现的是一行。

我认为由于第一个命令是在 truncate 模式下打开/dev/stdout,然后该模式由第二个文件描述符(wtf.txt)继承,然后被完全擦除,但我想知道你们中的一些人可能会更好地解释它,如果这是正确的行为还是错误。

为了清楚起见,我使用的命令是不同的,但使用 echo 示例更容易理解。原始命令是一个需要输出文件作为参数的命令,因为我想要 stdout 上的输出,所以我将/dev/stdout 作为参数传递。可以使用命令 openssl rand -hex 4 -out /dev/stdout >> wtf.txt 验证相同的行为。

最后,我设法通过以下方式解决了将附加操作委托(delegate)给 tee 的问题的解决方案:{ echo wtf > /dev/stdout } | tee -a wtf.txt > /dev/null

最佳答案

您可以使用 strace 检查会发生什么:

strace -o wtf-trace.txt -ff bash -c '{ (echo wtf) > /dev/stdout; } >> wtf.txt'

在我的例子中,这将生成两个文件,如 wtf-trace.txt.12889wtf-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。此时它 fork (克隆),等待它退出并退出自身。

第二个进程 { 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 连接到管道,该管道不可搜索或可截断...

注意:我只显示了 strace 生成的文件的相关行。

关于bash - bash 重定向中的意外行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33563896/

10-10 17:38