GNU 查找并屏蔽某些 shell 的 {} - 哪个?

GNU 查找并屏蔽某些 shell 的 {} - 哪个?

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 +Isetopt ignore_braces或关闭它emulate sh。这些 shell{}在任何情况下都不会扩展,即使它是单词的子字符串(例如foo{}bar),因为在参数中常见使用findxargs

单一 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,因为该文本于fish2005 年首次发布(而该文本或类似文本在 1994 年就已经存在),并且rc最初并不是 Unix shell。

有传言称某些版本csh(引入大括号扩展的 shell)需要它。但很难给予这些荣誉,因为csh2BSD 的第一个版本没有。这里是在 PDP11 模拟器中测试的:

# echo find -exec {} \;
find -exec {} ;

还有2BSD 的手册页csh明确指出:

作为特殊情况,“{”、“}”和“{}”不受干扰地传递。

因此,如果 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

但这是将一个参数传递给外部回声。

cshor中tcsh,您需要引用{}when 而不是单独引用,如下所示:

find . -name '*.txt' -type f -exec cp {} '{}.back' \;

(尽管这种find用法不可移植,因为某些finds 仅{}在单独扩展时才扩展)。

答案3

总之,cshbash和其他现代 shell 认识到用户可能并不要求空大括号扩展。 (现代csh实际上tcsh并且{}现在也可能可以理智地处理。)

相关内容