GNU find 的手册页指出:
-exec command ; [...] The string `{}' is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a `\') or quoted to protect them from expansion by the shell.
这是来自 (GNU findutils) 4.4.2 的 man find
。
现在我用 bash 和 dash 测试了这一点,两者都不需要被{}
屏蔽。这是一个简单的测试:
find /etc -name "hosts" -exec md5sum {} \;
是否有一个外壳,我确实需要遮盖牙套?请注意,它不取决于找到的文件是否包含空白(从 bash 调用):
find ~ -maxdepth 1 -type d -name "U*" -exec ls -d {} \;
/home/stefan/Ubuntu One
如果找到的文件被传递到子 shell,情况会发生变化:
find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d {}' \;
ls: cannot access /home/stefan/Ubuntu: No such file or directory
ls: cannot access One: No such file or directory
可以通过以下方式解决:
find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "$0"' {} \;
相比之下:
find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "{}"' \;
/home/stefan/Ubuntu One
但这不是手册页讨论的内容,是吗?那么哪个 shell{}
以不同的方式处理呢?
答案1
概括:如果曾经有一个可扩展的外壳{}
,那么它现在确实是旧的遗留东西了。
在 Bourne shell 和兼容 POSIX 的 shell 中,大括号 ({
和}
) 是普通字符(与 和 不同,(
和)
是单词分隔符,如;
和&
,而[
和]
是通配符)。以下字符串都应该按字面打印:
$ echo { } {} {foo,bar} {1..3}
{ } {} {foo,bar} {1..3}
由单个大括号组成的单词是保留字,仅当它是命令的第一个单词时才特殊。
Ksh 将大括号扩展实现为 Bourne shell 的不兼容扩展。这可以通过 关闭set +B
。 Bash 在这方面模仿了 ksh。 Zsh 也实现了大括号扩展;可以用set +I
或setopt ignore_braces
或关闭它emulate sh
。这些 shell{}
在任何情况下都不会扩展,即使它是单词的子字符串(例如foo{}bar
),因为在参数中常见使用find
和xargs
。
单一 Unix v2注意到
在某些历史系统中,花括号被视为控制运算符。为了协助未来的标准化活动,可移植应用程序应避免使用不带引号的大括号来表示字符本身。 ISO/IEC 9945-2:1993 标准的未来版本可能会要求这样做,
{
并将}
其单独视为控制运算符,尽管{}
由于经常使用的find
{}
构造,令牌可能会成为特殊情况的豁免。
该注释在该标准的后续版本中被删除;这的例子find
具有未引用的用途{}
,就像的例子xargs
。可能必须{}
引用历史上的 Bourne shell,但它们现在已经是非常旧的遗留系统了。
我手头的 csh 实现(OpenBSD 4.7,Debian 上的 BSD csh, tcsh) 全部扩展{foo}
为foo
但{}
不理会。
答案2
在3.0.0 之前的 shell版本中需要{}
引用。fish
$ fish -c 'echo find -exec {} \;'
find -exec ;
在 rc shell 中(也akanga
基于rc
,但不是es
):
$ rc -c "echo find -exec {} ';'"
line 1: syntax error near '{'
这些可能不是 GNU find 文档的作者在编写该文本时所想到的 shell,因为该文本于fish
2005 年首次发布(而该文本或类似文本在 1994 年就已经存在),并且rc
最初并不是 Unix shell。
有传言称某些版本csh
(引入大括号扩展的 shell)需要它。但很难给予这些荣誉,因为csh
2BSD 的第一个版本没有。这里是在 PDP11 模拟器中测试的:
# echo find -exec {} \;
find -exec {} ;
作为特殊情况,“{”、“}”和“{}”不受干扰地传递。
因此,如果 csh 或 tcsh 的后续版本后来打破了这一点,我会觉得很奇怪。
它可能是为了解决某些版本中的一些错误。仍然使用 2BSD csh(在 2.79BSD、2.8BSD、2.11BSD 中是相同的):
# csh -x
# echo foo {} bar
echo foo {} bar
foo {} bar
# echo `echo foo {} bar`
echo `echo foo {} bar`
echo foo {} bar
foo bar
但引用并没有帮助:
# echo `echo foo '{}' bar`
echo `echo foo '{}' bar`
echo foo {} bar
foo bar
您可以引用整个命令替换:
# echo "`echo foo {} bar`"
echo `echo foo {} bar`
echo foo {} bar
foo {} bar
但这是将一个参数传递给外部回声。
在csh
or中tcsh
,您需要引用{}
when 而不是单独引用,如下所示:
find . -name '*.txt' -type f -exec cp {} '{}.back' \;
(尽管这种find
用法不可移植,因为某些find
s 仅{}
在单独扩展时才扩展)。
答案3
总之,csh
。 bash
和其他现代 shell 认识到用户可能并不要求空大括号扩展。 (现代csh
实际上tcsh
并且{}
现在也可能可以理智地处理。)