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

use*_*own 37 shell find xargs quoting

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. 
Run Code Online (Sandbox Code Playgroud)

那是从人到find(GNU findutils)4.4.2。

现在我用 bash 和 dash 测试了这个,两者都不需要{}被屏蔽。这是一个简单的测试:

find /etc -name "hosts" -exec md5sum {} \; 
Run Code Online (Sandbox Code Playgroud)

是否有一个外壳,我真的需要掩盖大括号?请注意,它不取决于找到的文件是否包含空白(从 bash 调用):

find ~ -maxdepth 1 -type d -name "U*" -exec ls -d {} \; 
/home/stefan/Ubuntu One
Run Code Online (Sandbox Code Playgroud)

如果找到的文件被传递到子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
Run Code Online (Sandbox Code Playgroud)

可以通过以下方式解决:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "$0"' {} \;
Run Code Online (Sandbox Code Playgroud)

相比之下:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "{}"' \; 
/home/stefan/Ubuntu One
Run Code Online (Sandbox Code Playgroud)

但这不是手册页所讨论的内容,是吗?那么哪个 shell 的处理{}方式不同呢?

Gil*_*il' 28

简介: 如果曾经有一个可扩展的 shell {},那么它现在真的是老旧的东西了。

在 Bourne shell 和符合 POSIX 的 shell 中,大括号 ( {and }) 是普通字符(不同于(and)是单词分隔符,如;and &,and[和 and]是通配符)。以下字符串都应该按字面打印:

$ echo { } {} {foo,bar} {1..3}
{ } {} {foo,bar} {1..3}
Run Code Online (Sandbox Code Playgroud)

由单个大括号组成的单词是保留字,只有在它是命令的第一个单词时才特殊。

Ksh 将大括号扩展实现为与 Bourne shell 不兼容的扩展。这可以关闭set +B。Bash 在这方面模仿了 ksh。Zsh 也实现了大括号扩展;在那里它可以用set +Isetopt ignore_braces或关闭emulate sh。这些炮弹都没有扩大{}在任何情况下,即使它是一个字(如的一个子foo{}bar),由于参数共同使用findxargs

Single Unix v2指出

在某些历史系统中,花括号被视为控制运算符。为了协助未来的标准化活动,可移植应用程序应避免使用不带引号的大括号来表示字符本身。这可能是在ISO / IEC 9945-2的未来版本:1993年标准可能要求{}单独视为控制操作,虽然令牌{}可能是因为经常使用的从这个特殊的情况下豁免find {}结构。

此注释在标准的后续版本中被删除;的示例find有未加引号的 使用{}, 的示例也是如此xargs。可能必须{}引用历史上的 Bourne shell ,但它们现在已经是非常古老的遗留系统了。

我手头的 csh 实现(OpenBSD 4.7,Debian 上的 BSD csh,tcsh)都扩展{foo}foo{}不理会。

  • 那是`bash`(参见`$BASH_VERSION`)。支撑扩展非常活跃。 (2认同)
  • 那*是*重点。`{}` 语法起源于 `csh`,但 `{}` 扩展为空字符串。较新的 shell 认识到这是荒谬的,但仍然有一些旧的 `csh` 存在。 (2认同)

Sté*_*las 15

{}需要的版本被引用fish之前3.0.0外壳。

$ fish -c 'echo find -exec {} \;'
find -exec  ;
Run Code Online (Sandbox Code Playgroud)

在 rc shell 中(也akanga基于rc,但不是基于es):

$ rc -c "echo find -exec {} ';'"
line 1: syntax error near '{'
Run Code Online (Sandbox Code Playgroud)

fish2005 年首次发布以来,这些可能不是 GNU find 文档的作者在编写该文本时所考虑的 shell (而该文本或类似文本在 1994 年已经存在)并且rc最初不是 Unix shell。

有一些传言说csh(引入大括号扩展的外壳)的某些版本需要它。但是因为csh2BSD的第一个版本没有,所以很难相信那些。在 PDP11 模拟器中测试如下:

# echo find -exec {} \;
find -exec {} ;
Run Code Online (Sandbox Code Playgroud)

来自 2BSDcsh手册页明确指出

作为特殊情况`{',`}' 和`{}' 不受干扰地传递。

因此,如果后续版本的 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
Run Code Online (Sandbox Code Playgroud)

引用并没有帮助:

# echo `echo foo '{}' bar`
echo `echo foo '{}' bar`
echo foo {} bar
foo  bar
Run Code Online (Sandbox Code Playgroud)

您可以引用整个命令替换:

# echo "`echo foo {} bar`"
echo `echo foo {} bar`
echo foo {} bar
foo {} bar
Run Code Online (Sandbox Code Playgroud)

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

cshor 中tcsh,您需要引用{}when not in its own like in:

find . -name '*.txt' -type f -exec cp {} '{}.back' \;
Run Code Online (Sandbox Code Playgroud)

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


gee*_*aur 1

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

  • 不。我正在与一个人争论,他说,在我们的 wiki 中,我们应该始终提供使用“{}”的建议,因为手册页是这么说的。现在我想知道是否有一个我没有使用的 shell,比如我不知道的 ksh、zsh、tcsh、csh 或 XYZsh,这个说法是否正确,或者我是否可以诚实地假设,没有。如果不同 Unix 上的 shell 需要“{}”,这将很好地解释为什么这句话仍在手册页中,但不适合作为 Linux 初学者的建议。 (4认同)
  • Pdksh 与 mksh、ksh93、bash 和 zsh 一样,仅当中间有逗号时才展开大括号(对于 ksh93、bash 和 zsh 则为“..”)。只有 (t)csh 将 `{foo}` 扩展为 `foo`,甚至它也保留了 `{}`(至少在最近的 BSD 上)。 (4认同)