![作为 for 循环的一部分写入同一文件会生成一个空文件](https://linux22.com/image/174893/%E4%BD%9C%E4%B8%BA%20for%20%E5%BE%AA%E7%8E%AF%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%E5%86%99%E5%85%A5%E5%90%8C%E4%B8%80%E6%96%87%E4%BB%B6%E4%BC%9A%E7%94%9F%E6%88%90%E4%B8%80%E4%B8%AA%E7%A9%BA%E6%96%87%E4%BB%B6.png)
我正在编写一个命令,将一些文本添加到一堆与特定模式匹配的文件中:
for file in $(find . -name "*.txt"); do sed "1s/^/hello/" $file; done
如果我写入stdout
或不同的文件(例如"$file.bak"
),一切都会按计划进行,但是当我尝试让命令写回同一个文件时,文件的内容会被擦除:
for file in $(find . -name "*.txt");do sed "1s/^/hello/" $file > $file; done
我发现这种行为令人惊讶,有人可以从概念上解释为什么会发生这种情况吗?我在 macOS 上尝试过此操作bash
并zsh
达到相同的效果。
(请注意,我知道sed -i ""
就地写入,但我想在就地写入之前将结果通过管道传输到不同的命令。)
答案1
概念上的惊喜可能就在这个声明中
但是当我尝试将命令写回同一个文件时
使用时,cmd file > file
它不是cmd
打开文件来写回,而是打开 shell 本身。 shell 看到时所做的第一件事> file
就是在位置 0 处以写入模式打开文件(从而截断它),然后将 stdout 重定向到该文件。所以此刻cmd
开始读取file
它已经是空的。
或者,更正式一点:
- Shell fork 一个新进程来
cmd
运行 - 新进程(仍在运行 shell 代码)打开
file
以在位置 0 写入(从而截断它) - 新进程执行
cmd
,实际上在当前进程中启动一个新的二进制文件 cmd
运行时标准输出指向file
cmd
打开file
阅读,但此时已经空了
另一方面,如果您使用的sed -i '1s/^/hello/' file
文件是单独管理的sed
(从技术上讲,它会在后台创建一个临时文件)。