我正在编写一个命令,将一些文本添加到一堆与特定模式匹配的文件中:
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
(从技术上讲,它会在后台创建一个临时文件)。