为什么有时元字符周围需要空格?

Why is whitespace sometimes needed around metacharacters?

提问人:spydon 提问时间:1/17/2014 最后编辑:codeforesterspydon 更新时间:8/1/2018 访问量:81064

问:

几个月前,我在胳膊上纹了一个叉子炸弹,我跳过了空格,因为我认为没有它们看起来更好。但令我沮丧的是,有时(并非总是)当我在 shell 中运行它时,它不会启动分叉炸弹,但它只是给出语法错误。

bash: syntax error near unexpected token `{:'

昨天当我尝试在朋友的 Bash shell 中运行它时发生了这种情况,然后我添加了空格,它突然起作用了,而不是:(){ :|:& };::(){:|:&};:

空白重要吗?我的胳膊上有语法错误吗?!

它似乎总是在 zsh 中工作,但在 Bash 中不起作用。

一个相关的问题没有解释任何关于空格的事情,这确实是我的问题;为什么 Bash 需要空格才能正确解析它?

bash shell 语法错误

评论

6赞 Benoit 1/22/2014
在这里发布了同样的问题(不包括纹身部分)。
3赞 Martin Tournoij 1/24/2014
此外,冒号 (:) 不能用作函数名称(请参阅:pubs.opengroup.org/onlinepubs/9699919799/utilities/...FreeBSD 的 /bin/sh 甚至对此给出了一个错误......
5赞 Dennis 5/5/2014
@Carpetsmoker:我不确定这有什么关系。这个问题是关于 Bash 的。

答:

50赞 Peter Westlake 1/17/2014 #1

大括号更像是奇数关键字,而不是特殊符号,并且确实需要空格。例如,这与括号不同。比较:

(ls)

有效,以及:

{ls}

它查找名为 的命令。要工作,它必须是:{ls}

{ ls; }

分号停止将右大括号作为 的参数。ls

你所要做的就是告诉人们你使用的是比例字体,具有相当窄的空格字符。

评论

12赞 Peter Westlake 1/17/2014
@DmitriChubarov - 这是一个非常狡猾的伎俩,使用了完全不同含义的牙套。它扩展了逗号分隔的值列表,在本例中只是 .ls
7赞 Alfe 1/17/2014
但。。。男孩。。。你将在你的余生中拥有纹身!你把自己标记为一个书!然后你甚至不能在缝合之前把它弄好?我想这比书要多走两步;-)没有冒犯,伙计,我只是想知道。
15赞 spydon 1/17/2014
@Alfe我在纹身之前尝试过它,但我在我的 zsh 中尝试过,我认为它与 bash 具有相同的解析。我太傻了,但我只会告诉人们这是 zsh。:)
20赞 poke 1/18/2014
@spydon 或者你告诉他们,你故意遗漏了这个空间,这样从你的纹身中复制命令的人就不会意外地运行它并使他们的机器崩溃;)
5赞 Adam Miller 1/24/2014
嘿,如果它在 zsh 中有效,为什么不直接在它的上方(或之前)添加 #!/bin/zsh?无论如何,最好先指定 shell。
40赞 devnull 1/17/2014 #2

然后我添加了空格,它突然起作用了......

这是因为 shell 的解析方式。在函数定义开始之后,即在 .{

foo() { echo hey& }
foo() { echo hey&}
foo(){ echo hey&}

是有效的。另一方面

foo() {echo hey&}

不是。


你实际上需要一个这样的tatoo:

enter image description here


源头

  /* We ignore an open brace surrounded by whitespace, and also
     an open brace followed immediately by a close brace preceded
     by whitespace.  */

省略 后面的空格会导致 被解释为单个标记。{{echo


等效形式的

:(){ :|:& };:

:(){
:|:& };:

请注意,备用版本中没有空格,但换行符会导致 shell 识别为标记。{{

评论

0赞 Jonah 11/22/2019
“在 { 之后省略一个空格会导致 {echo 被解释为单个标记”——那么解析器是否认为它在到达所需的左大括号之前遇到了调用的命令?{echo
0赞 cole 10/25/2021
哈哈。那张照片,恰好是我的脖子:)
272赞 Dima Chubarov 1/17/2014 #3

在 BASH 中有一个分隔标记的字符列表。这些字符称为元字符,它们是 、 、 、 、 、 制表符。另一方面,大括号 ( 和 ) 只是构成单词的普通字符。|&;()<>{}

省略前面的第二个空格就可以了,因为是一个元字符。因此,你的纹身应该至少有一个空间字符。}&

:(){ :|:&};:

评论

36赞 1/23/2014
简单的解决方案:将taoo的第一部分向左移动,并在两个部分之间移植皮肤。
23赞 anishsane 6/9/2014
我喜欢这个词,......双关?;) :D(对不起,偏离主题的评论。on the other hand
4赞 tfogo 6/9/2014
但这对 zsh 来说是不同的吗?zsh 在哪些方面有所不同?
2赞 mklement0 2/19/2016
很好的解释,除了 和 是 shell 关键字。只有当它们没有被识别出来时——由于缺乏周围的空间/元字符——它们才会被视为它们所属单词的字面部分。(然后是大括号扩展,这发生在不同的上下文中。{}
2赞 chepner 9/20/2016
@DmitriChubarov 命令名称中允许使用大括号(包括函数:.“名称”,如 by 中定义为“仅由字母数字字符和下划线组成,并以字母字符或下划线开头的单词”,仅适用于变量名称。{foo} () { echo hello; }bash
41赞 Jerry Coffin 1/22/2014 #4

虽然在 tatoo 字体中不容易看到,但大括号和冒号之间实际上有一个字节顺序标记 (BOM)(当你拿到 tatoo 时,你可能已经陶醉了,以至于你没有注意到它,但它确实在那里)。这留下了三种明显的可能性:

  1. 您在转录代码时未能键入 BOM。其结果是GIGO的明显应用。shell 根本无法识别失败的听录中不存在的 BOM。
  2. 你的壳太旧了。它无法识别 Unicode 字符,因此 BOM(可能还有所有其他 Unicode 字符)被完全忽略,即使文件开头以外的任何位置的 BOM 都应该被视为零宽度、不间断的空格。
  3. 你的外壳太新了。不推荐将 BOM 用作 ZWNBS,作者已经实现了 Unicode 的未来版本,其中不再允许这种用法。
85赞 SzG 1/29/2014 #5

只需纹身

#!/bin/zsh

Shebang 在上面,你会没事的。

评论

6赞 SzG 6/10/2014
如果我们挑剔,shebang 根本不起作用,因为 shell,希望是 zsh,处于交互模式,正如提示所证明的那样......