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

550

几个月前,我在手臂上纹了一个fork bomb图案,并省略了空格,因为我认为没有空格更好看。但令我沮丧的是,有时(不总是),当我在shell中运行它时,它不会启动fork bomb,而只是显示语法错误。

bash: syntax error near unexpected token `{:'

昨天当我在朋友的Bash shell中尝试运行它时,加上一个空格后它突然就成功了,用 :(){ :|:& };: 代替 :(){:|:&};:

这个空格有影响吗?我是不是在我的手臂上刻了一个语法错误?!

zsh中似乎总是有效,但在Bash中不行。

相关问题并未解释任何关于空格的内容,这确实是我的问题;为什么Bash需要这个空格才能正确解析它?


6
我已在此处发布了同样的问题(不包括纹身部分)。 - Benoit
3
另外,冒号(:)不能用作函数名(参见:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01)。即使在FreeBSD的/bin/sh中使用,也会出现错误。 - Martin Tournoij
5
@Carpetsmoker: 我不确定这与问题的相关性。这个问题是关于Bash的。 - Dennis
5个回答

272

在BASH中,有一些用于分隔标记的字符列表。这些字符称为元字符,它们包括|&;()<>空格制表符。另一方面,花括号({})只是组成单词的普通字符。

由于&是元字符,因此可以省略}前的第二个空格。因此,您的纹身应该至少有一个空格字符。

:(){ :|:&};:

36
简单解决方法:将taoo的前半部分向左移动,并在两个部分之间移植皮肤。 - user1779715
23
我喜欢“另一方面”的说法... 是双关语吗?;) :D(抱歉,打了个离题的评论。) - anishsane
4
对于zsh来说,情况是不同的。那么zsh有何不同之处呢? - tfogo
2
很好的解释,除了在这种情况下 {} 是 _shell 关键字_。只有当它们由于缺乏周围空格/元字符而未被识别为此类关键字时,它们才被视为成为单词一部分的文字。 (然后还有花括号扩展,发生在不同的上下文中。) - mklement0
1
@mklement0 谢谢,好观点,{} 是单个字符关键字。将它们与其他字符组合成更长的单词几乎没有用处,因为名称中不允许使用大括号。但是,您需要使用分隔符将它们分开。 - Dima Chubarov
2
@DmitriChubarov 大括号在命令名称中是允许的(包括函数:{foo} () { echo hello; })。"名称" 在 bash 中的定义为 "由字母数字字符和下划线组成,并以字母字符或下划线开头的单词",仅适用于变量名称。 - chepner

85

只是纹身而已。

#!/bin/zsh

在其上方添加shebang并且你就可以了。


6
如果我们要挑剔的话,那么这个 shebang 将不起作用,因为 shell(希望是 zsh)处于交互模式,这一点可以从提示符上看出来。 - SzG

50

大括号更像是奇怪的关键词,而不是特殊符号,并且需要空格。这与括号不同,例如:

(ls)

可以正常工作:

{ls}

该命令寻找名为{ls}的命令。为了正常工作,它必须:

{ ls; }

分号的作用是防止闭合括号被视为 ls 的参数。

你只需要告诉人们你正在使用一个具有相对较窄空格字符的比例字体即可。


12
@DmitriChubarov - 这是一个非常狡猾的技巧,使用完全不同意义的大括号。它会展开逗号分隔的值列表,而在这种情况下仅为 ls - Peter Westlake
7
但是... 男孩... 你将终身拥有这个纹身!你为自己标上了"书呆子"的标签,而且在刺青之前连设计都没弄对?我猜这已经超越了书呆子的范畴了;-) 不冒犯你啊,伙计,我只是好奇。 - Alfe
15
@Alfe 我在纹身前就试过,但我是在我的zsh中尝试的,我以为它有与bash相同的解析。我真是太傻了,但我只会告诉人们这是zsh :) - spydon
20
@spydon 或者你告诉他们,你故意省略了空格,这样从你的纹身复制命令的人就不会意外运行它并导致机器崩溃;) - poke
5
嘿,如果在zsh中有效,为什么不在它上面(或之前)添加#!/bin/zsh呢?无论如何,首先指定shell是一个好习惯。 - Adam Miller
显示剩余4条评论

41

虽然在文字纹身字体中不容易看到,但大括号和冒号之间实际上有一个字节顺序标记(BOM)(当您施瓦辛格式醉酒时,可能没有注意到它,但确实存在)。这留下了三种明显的可能性:

  1. 当你转录代码时,你没能输入BOM。结果是“垃圾进去,垃圾出来”的明显应用程序。shell根本无法识别不存在于转录失败中的BOM。
  2. 您的shell太旧了。它不识别Unicode字符,因此BOM(以及可能的所有其他Unicode字符)完全被忽略,即使在文件开头以外的位置使用BOM也应该被视为零宽度、非断开空格。
  3. 您的shell太新了。作为ZWNBS的BOM的使用已被弃用,作者们已经实现了Unicode的未来版本,其中不再允许此用法。

40

我加了空格,然后它就突然正常了......

这是因为shell的解析方式。在函数定义开始后,即在{之后需要一个空格。

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

是有效的。另一方面,

foo() {echo hey&}

不是。


你实际上需要一个像这样的纹身:

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识别 { 为一个标记。


在 { 后面省略空格会导致 {echo 被解释为单个标记。因此,解析器会认为在到达所需的开括号之前已经遇到了名为 {echo 的命令? - Jonah
LOL。那张照片恰好是我的脖子 :) - Joe Steeve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接