我正在阅读手册页,find
发现自己对以下命令感到困惑。一个和它对应的一个有什么区别。
以下两个命令有什么区别:
find -execdir command "{}" \; find -execdir "command {}" \;
混淆的原因:我认为引用应该指示外壳将引用的部分作为一个块。因此,当我看到第二个时,我认为它会失败,因为没有命令command <file-name>
。
以下两者有什么区别:
find -execdir bash -c "command" "{}" \; find -execdir bash -c "command {}" \;
混淆的原因:根据我的理解,在第二个版本中将命令与花括号一起括起来应该作为一个整体传递给 bash 命令,并且find
不应该用相应的文件名解释大括号。
以下两者有什么区别:
find -execdir bash -c "something \"$@\"" {} \; find -execdir bash -c 'something "$@"' bash {} \;
据我了解,两者是相同的。将大括号传递给 shell,第二个版本,如何比第一个更安全。
更新
刚刚注意到问题#3 的第一个版本的命令不起作用!尝试了以下(没有工作):
find -execdir bash -c 'something \"$@\"' {} \; # changed external quotes to single ones.
find -execdir bash -c "something $@" {} \; # removed the inner quotes.
find -execdir bash -c 'something $@' {} \; # same as above but with outer single quotes instead.
我在这里想念什么?我相信我应该能够将大括号从包含命令的引号中去掉?
这(正如您所注意到的)相当复杂。我会试着解释一下。根据命令经过的解析/处理顺序进行思考,并观察每一步发生的情况是有帮助的。通常,该过程如下所示:
find
搜索文件,并将其“-execdir
”和“;
”之间的内容作为命令运行。请注意,它将“{}
”替换为匹配的文件名,但不进行其他解析——它只是运行“-execdir
”之后的第一个 arg 作为命令名,并将以下参数作为其参数传递给它。bash
并且它被传递了-c
选项,它会立即将参数解析-c
为命令字符串(有点像微型 shell 脚本),并将其其余参数作为该迷你脚本的参数。好的,在我深入研究之前还有一些注意事项:我正在使用 BSD
find
,它要求明确指定要搜索的目录,所以我将使用find . -execdir ...
而不是find -execdir ...
. 我在一个包含文件“foo.txt”和“z@$%^;*;echo wheee.jpg”的目录中(以说明使用bash -c
错误的风险)。最后,我pargs
在我的二进制目录中调用了一个简短的脚本,它打印了它的参数(或者如果没有得到任何参数就会抱怨)。问题一:
现在让我们试试第一个问题中的两个命令:
这符合您的期望:第一个有效(顺便说一句,您可以省略双引号
{}
),第二个失败,因为空格和文件名被视为命令名称的一部分,而不是它的参数。顺便说一句,也可以使用
-exec[dir] ... +
而不是-exec[dir] \;
-- 这告诉find
尽可能少地运行命令,并一次传递一堆文件名:问题二:
这次我将一次选择一个选项:
“嗯”,你说?这里发生的是使用“ ”、“ ”、“ ”之
bash
类的参数列表运行。该选项告诉运行它的下一个参数(“pargs”),就像一个微型 shell 脚本,像这样:-c
pargs
foo.txt
-c
bash
...然后将“迷你脚本”传递给参数“foo.txt”(稍后会详细介绍)。但是那个迷你脚本并没有对它的参数做任何事情——具体来说,它不会将它们传递给
pargs
命令,所以pargs
什么都看不到。(我将在第三个问题中找到正确的方法。)现在,让我们尝试第二个问题的第二个替代方案:现在一切都在起作用,但只是在某种程度上。
bash
使用参数“-c
”和“pargs”+文件名运行,这与“。”的预期相同。和“foo.txt”,但是当你传递bash
参数“-c
”和“pargs z@$%^;*;echo wheee.jpg
”时,它现在运行相当于它的迷你脚本:因此 bash 会将其拆分为三个用分号分隔的命令:
pargs z@$%^
”(你看到的效果)*
",扩展为单词 "foo.txt
" 和 "z@$%^;*;echo wheee.jpg
",因此尝试foo.txt
作为命令运行并将另一个文件名作为参数传递给它。没有该名称的命令,因此它给出了适当的错误。echo echo wheee.jpg
”,这是一个非常合理的命令,正如你所见,它会在终端上打印“wheee.jpg”。因此它适用于具有纯名称的文件,但是当它遇到包含 shell 语法的文件名时,它开始尝试执行部分文件名。这就是为什么这种做事方式不被认为是安全的。
问题三:
同样,我将一次查看一个选项:
再一次,我听到你说“嗯????” 这里最大的问题是它
$@
没有转义或单引号,因此它在传递给find
. 我将在这里pargs
展示find
实际得到的参数:请注意,
$@
刚刚消失了,因为我在一个没有收到任何参数(或使用set
命令设置它们)的交互式 shell 中运行它。因此,我们正在运行这个迷你脚本:...这解释了为什么
pargs
得到一个空参数。如果这是在已接收参数的脚本中,事情会更加混乱。转义(或单引号)
$
解决了这个问题,但仍然不太有效:这里的问题是
bash
将迷你脚本之后的下一个参数视为迷你脚本的名称(迷你脚本可用作$0
,但不包含在 中$@
),而不是常规参数(即$1
)。这是一个演示这个的常规脚本:现在用一个类似
bash -c
的小脚本试试这个:解决这个问题的标准方法是添加一个虚拟脚本名称参数(如“bash”),以便实际参数以通常的方式显示:
这正是您的第二个选项所做的,将“bash”作为脚本名称传递,找到的文件名传递为
$1
:这终于奏效了——真的,即使是奇怪的文件名。这就是为什么这(或您的第一个问题中的第一个选项)被认为是使用
find -exec[dir]
. 您也可以将其与以下-exec[dir] ... +
方法一起使用: