为什么shell重定向会与find发生奇怪的交互

为什么shell重定向会与find发生奇怪的交互,shell,find,sh,Shell,Find,Sh,我想在*nix目录和任何子目录中的每个.java文件前面添加一个许可证文件。我有这样一个解决方案,似乎效果不错: $ cat muppet-license.txt // LICENSE: // Manuh-manuh $ for file in `find . -iname "*.java"`; do cat muppet-license.txt "$file" > "$file.out"; mv "$file.out" "$file"; done 我的问题是,

我想在*nix目录和任何子目录中的每个.java文件前面添加一个许可证文件。我有这样一个解决方案,似乎效果不错:

$ cat muppet-license.txt 
// LICENSE: // Manuh-manuh
$ for file in `find . -iname "*.java"`; do 
     cat muppet-license.txt "$file" > "$file.out"; 
     mv "$file.out" "$file";
done
我的问题是,为什么以下查找调用不起作用:

find . -iname "*.java" -exec sh -c 'cat muppet-license.txt "$1" > "$1"' -- {} \;
这导致find找到的第一个文件的前面反复添加了“木偶许可证”-该文件似乎在不断增长,没有停止


有人能解释一下造成这种差异的原因吗?这是否与修改名为$1的文件有关,导致find作为递归搜索的一部分重新查找该文件?有人对find使用的算法的详细信息有什么好的参考吗?

您正在告诉cat从您正在写入的同一文件中读取:

cat muppet-license.txt "$1" > "$1"
因此cat将读取
muppet license.txt
并将其写入
$1
,然后读取
$1
(现在包含许可证)并再次将许可证附加到同一文件的末尾,然后继续读取已写入的内容,依此类推,在一个无休止的循环中,在
$1
中反复复制许可证文件

像这样的方法应该会奏效:

find . -iname "*.java" -exec sh -c 'cat muppet-license.txt "$1" > "$1.out"; mv "$1.out" "$1"' -- {} \;
通过将许可证和
$1
写入一个单独的文件
$1.out
并在写入后将其移回
$1
,可以避免无休止的循环。区别与find无关,只与sh和cat的调用有关。

问题在于

cat foo $file > $file

你让它做什么,它就会做什么,这并不总是显而易见的。shell使用该命令所做的第一件事是打开
$file
进行写入,从而将其截断为零长度。然后运行
cat
,将
foo
$file
的内容连接到
$file

中,这些内容现在是foo的副本。问题不在于find。如果您执行以下操作,则会发生相同的行为:

cat muppet-license.txt a.java > a.java

这是因为cat正在重新读取它正在写入的数据。shell打开a.java进行编写,将其截断为零长度。cat然后将muppet-license.txt的内容写入a.java,然后打开a.java(现在是muppet-license.txt的副本),并将第一行写入文件末尾(第二行)。然后,它读取第2行并将其附加到末尾(写入第3行),并不断重复。

我明白了。这不是我问的问题。你读对了吗?要么你误解了我(和其他人)的回答,要么你需要编辑来澄清你的问题。稍微修改了我的回答,使意思更明确。@kittylyst:什么评论?你是指我的回答吗?我的回答你觉得困惑吗?shell截断a.java,丢弃其中的所有数据。然后,cat进入一个无止境的循环,永远不会终止(直到文件系统已满或发生其他错误)。