find . -name "FILENAME" -exec rm {} \;
的语法主要是因为我看不到
-exec
部分的工作原理。花括号,反斜杠和分号的含义是什么?该语法还有其他用例吗?#1 楼
答案来自以下部分:-exec
的基本用法结合使用
-exec
和sh -c
结合使用
-exec ... {} +
/>
使用
-execdir
-exec
的基本用法-exec
选项采用带有可选参数的外部实用程序作为其参数并执行它。如果字符串
{}
存在于给定命令的任何位置,则它的每个实例都将被当前正在处理的路径名替换(例如./some/path/FILENAME
)。在大多数Shell中,不需要将两个字符{}
引起来。该命令需要以
;
终止,以便find
知道它的结束位置(因为之后可能会有其他选择)。为了保护;
不受外壳影响,需要将其引用为\;
或';'
,否则外壳会将其视为find
命令的结尾。示例(第一个结尾处的
\
两行只是连续的行):然后,它将使用-type f
(不会产生任何输出,只是退出状态)测试字符串*.txt
是否出现在找到的任何文件中。对于包含字符串的那些文件,将执行hello
将文件的内容输出到终端。每个grep -q
都对cat
找到的路径名像“ test”一样,就像-exec
而find
可以。如果该命令返回零退出状态(表示“成功”),则考虑-type
命令的下一部分,否则-name
命令以下一个路径名继续。上面的示例中使用它来查找包含字符串find
的文件,但忽略所有其他文件。上面的示例说明了
find
的两个最常见的用例:作为进一步限制搜索的测试。
对找到的路径名执行某种操作(通常(但不一定是在
hello
命令的末尾)。结合使用
-exec
和find
-exec
可以执行的命令仅限于带有可选参数的外部实用程序。不能直接与sh -c
一起使用shell内置函数,条件,条件,管道,重定向等,除非包裹在-exec
子外壳之类的东西中。如果需要
-exec
功能,请使用sh -c
代替bash
。bash -c
运行sh -c
并在命令行中提供了脚本,随后是该脚本的可选命令行参数。使用
sh -c
的简单示例由本身,没有/bin/sh
:find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
这会将两个参数传递给子shell脚本。这些将放置在
sh -c
和find
中供脚本使用。字符串
。这将在脚本内以sh
的形式提供,并且如果内部外壳输出错误消息,则会在该字符串的前面加上前缀。参数
apples
在脚本中以
的形式提供,并且有更多的参数,那么它们本来可以作为
,
等获得。它们也将在列表"$@"
中可用(但"$@"
除外,后者不属于-exec
)。它使我们可以制作任意复杂的脚本,以对find
所找到的路径名起作用。后缀保留在变量中:sh -c 'echo "You gave me , thanks!"' sh "apples"
在内部脚本中,
是字符串text
,
是字符串txt
,
是为我们找到的任何路径名find
。参数扩展${3%.}
将采用路径名并从中删除后缀.text
。或者,使用dirname
/ basename
:from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "" "${3%.}."' sh "$from" "$to" {} ';'
,或者,在内部脚本中添加了变量的地方:在外部脚本中。
上面是从
from
和to
调用任意复杂脚本的正确方法。在类似find . -type f -name "*.$from" -exec sh -c '
mv "" "$(dirname "")/$(basename "" ".")."' sh "$from" "$to" {} ';'
的循环中使用
-exec
容易出错且不雅(个人见解)。它正在空格上分割文件名,调用文件名遍历,并且甚至在运行循环的第一次迭代之前,还强制Shell扩展find
的完整结果。 /> 为什么循环遍历find的输出错误做法?
可以安全地使用`find -exec sh -c`吗?
使用
find
/>
最后的
find
可能会替换为-exec ... {} +
。这使;
用尽可能多的参数(找到的路径名)执行给定命令,而不是为每个找到的路径名执行一次。字符串+
必须恰好在find
之前出现,这样才能起作用。 find . -type f -name "*.$from" -exec sh -c '
from=; to=; pathname=
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
这里,
{}
将收集生成的路径名并一次对尽可能多的路径名执行+
。for pathname in $( find ... ); do
与这里一样,
find
将执行最少的次数。最后一个示例需要coreutils的GNU cat
(支持mv
选项)。使用
mv
也是一种通过任意复杂的脚本循环遍历一组路径名的有效方法。基本用法与使用
-t
时相同,但是脚本现在需要更长的参数列表。这些可以通过在脚本内部遍历-exec sh -c ... {} +
来遍历。上一节中的示例更改了文件名后缀:
br />使用
-exec sh -c ... {} ';'
还有
"$@"
(由大多数-execdir
变体实现,但不是标准选项)。给定的shell命令以找到的路径名的目录作为它的当前工作目录执行,并且-execdir
将包含找到的路径名的基名而没有其路径(但是GNU find
仍将基名加上-exec
前缀,而BSD {}
不会做到这一点)。示例:
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
这会将每个找到的
find
-文件移动到同一目录中预先存在的./
子目录中文件所在的位置。该文件还将通过添加后缀find
重命名。文件的新名称。我们还需要*.txt
的目录名才能正确定位done-texts
目录。使用
.done
可以使诸如此类的事情变得更容易。必须使用子外壳:find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
或者,
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=; to=
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
评论
-exec接受程序和参数并运行它;一些shell命令仅由程序和参数组成,而许多则不包括。 Shell命令可以包括重定向和管道; -exec不能(尽管整个发现都可以重定向)。一个shell命令可以使用; &&如果等; -exec无法执行,尽管-a -o可以执行一些操作。 Shell命令可以是别名或Shell函数,也可以是内置命令。 -exec不能。 shell命令可以扩展vars。 -exec不能(尽管运行find的外壳可以)。 Shell命令每次可以用不同的$(command)替代; -exec不能。 ...
–dave_thompson_085
17年9月1日在16:02
说这是一个shell命令是错误的,找到-exec cmd arg \;不会调用Shell来解释Shell命令行,而是直接运行execlp(“ cmd”,“ arg”),而不是execlp(“ sh”,“-c”,“ cmd arg”)如果没有内置cmd,则最终执行等效于execlp(“ cmd”,“ arg”)的操作。
–StéphaneChazelas
17年9月26日在11:03
您可以阐明,所有查找参数在-exec之后至;或+组成要与命令一起执行的命令,将{}参数的每个实例替换为当前文件(带有;),并将{}作为最后一个参数,而+之前的+替换为文件列表作为单独的参数( (在{} +情况下)。 IOW -exec带有多个参数,以;终止;或{} +。
–StéphaneChazelas
17-09-26在11:25
@Kusalananda您的最后一个示例也不能使用以下更简单的命令:find。 -type f -name'* .txt'-exec sh -c“ mv $ 1 $(dirname $ 1)/ done-texts / $(basename $ 1).done” sh {}';' ?
– Atralb
20年7月8日在22:58
@Atralb是的,它也可以工作,并且与最后一段代码具有相同的效果,但是不是在循环中运行mv,而是对每个找到的文件执行一次,而是对每个找到的文件执行sh和mv,这很明显处理大量文件的速度较慢。
– Kusalananda♦
20年7月9日在6:52
评论
@Philippos:我明白你的意思。请记住,手册页是参考,即对于那些了解此事的人有用,以查找语法。对于刚接触该主题的人来说,他们通常是神秘且正式的才有用。您会发现接受的答案是手册页条目长度的大约10倍,这是有原因的。甚至旧的POSIX手册页都读取了一个Utility_name或仅包含两个字符“ {}”的自变量应替换为当前路径名,这对我来说似乎足够了。另外,它有一个-exec rm {} \;的示例,就像您的问题一样。在我的时代,除了“大灰墙”之外,几乎没有其他资源,这些手册是印刷的手册页(纸张比存储便宜)。因此,我知道这对于刚接触该主题的人就足够了。您的最后一个问题是公平的。不幸的是,@ Kusalananda和我自己都没有答案。
@Philippos :-)哦,尝试一下,这是令人兴奋的事情,因为“令人兴奋”的程度超出了我的图表范围。
Comeon @Philippos。您是真的告诉Kusalananda他的手册页没有改善吗? :-)
@ZsoltSzilagy我既没有告诉也没有那个意思。他确实很好地喂养了您,我只是认为您已经够大了,可以自己吃饭了。 (-;