bash如何区分大括号扩展和命令分组?


47

我注意到{可以在花括号扩展中使用:

echo {1..8}

或在命令分组中:

{ls;echo hi}

bash如何知道区别?


1
很好的问题,+ 1。现在看来似乎可能是{被解释为一个命令列表,如果它出现在命令的开始,作为一个括号扩展,否则,但我不知道。
Celada's

16
{ls;echo hi}不合法的bash。在左大括号之后需要一个空格,在右大括号之前需要一个分号。
PSkocik

Answers:


38

一个简化的原因是存在一个字符:space

括号扩展不处理(未引用)空格。

一个{...}列表需要(未报价)的空间。

更详细的答案是Shell如何解析命令行


解析(理解)命令行的第一步是将其分为几部分。
这些部分(通常称为单词或标记)是通过从链接中的每个元字符处划分命令行来产生的

  1. 将命令拆分为由固定的元字符集分隔的标记:SPACE,TAB,NEWLINE,;,(,),<,>,|和&。令牌的类型包括单词,关键字,I / O重定向器和分号。

元字符:spacetabenter;,<>|&

拆分后,单词可能是一种类型(如Shell所理解):

  • 指挥预分配: LC=ALL ...
  • 命令 LC=ALL echo
  • 争论 LC=ALL echo "hello"
  • 重新导向 LC=ALL echo "hello" >&2

支撑扩展

仅当“括号字符串”(不带空格或元字符)是单个单词(如上所述)且未加引号时,它才是“括号扩展”的候选者。稍后将在内部结构上执行更多检查。

因此,这:等于或(在bash中,zsh是不同的)被{ls,-l}称为“括号扩展”变为。ls -lfirst wordargument

$ {ls,-l}            ### executes `ls -l`
$ echo {ls,-l}       ### prints `ls -l`

但这不会:{ls ,-l}。Bash会继续分裂,space并将行解析为两个单词:{ls,-l}将会触发command not found(参数,-l}丢失):

 $ {ls ,-l}
 bash: {ls: command not found

您的一行:{ls;echo hi}由于存在两个元字符;和,因此不会成为“括号扩展” space

它将分为三个部分:{lsnew命令:echo hi}。了解;触发器将触发新命令的启动。{ls找不到该命令,下一条命令将显示hi}

$ {ls;echo hi}
bash: {ls: command not found
hi}

如果将其放在其他命令之后,它将始终在以下命令之后启动新命令;

$ echo {ls;echo hi}
{ls
hi}

清单

其中的“化合物的命令”的是“支撑列表”(我的话): { list; }
如您所见,它是用空格和closeing定义的;。需要
空格,;因为{}都是“保留 ”。

因此,要被识别为单词,必须将其用元字符包围(几乎总是:)space

链接页面的第 2点所述

  1. 检查每个命令的第一个标记以查看它是否为....,{或(,然后该命令实际上是复合命令。

您的示例:{ls;echo hi}不是列表。

它需要一个结束符,;并且至少要有一个空格{。最后一个}由结束定义;

这是一个清单{ ls;echo hi; }。这{ ls;echo hi;}也是(不太常用,但有效)(感谢@choroba的帮助)。

$ { ls;echo hi; }
A-list-of-files
hi

但是作为命令的参数(shell知道区别),它会触发错误:

$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'

但是请注意您认为外壳正在解析的内容:

$ echo { ls;echo hi;
{ ls
hi

2
这确实是最好的答案,因为您确实为我们提供了bash解析器的工作原理!并附有详细说明!
lovespring

2
;和之间不需要空格}{ ls;}因为分号已经是一个元字符,所以可以使用。
choroba

1
@lovespring谢谢,是的,我花了一些时间编写它。我很高兴知道它很有用。再次感谢。

优秀的文章,非常感谢参考文献
爱德华·托沃兹

16

block {是一个shell关键字,因此必须与下一个单词用空格隔开,在大括号扩展中,不应有空格(如果需要大括号扩展一个空格,则必须转义它:)echo {\ ,a}{b,c}

您可以在命令开头使用大括号扩展:

{ls,.}  # expands to "ls ."

但是,您不能使用它扩展到块,因为在扩展之前会进行分组命令的解析:

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory

5

通过检查命令行的语法可以知道。以相同的方式,它知道在表达式中echo echo,第一个回波应被视为命令,第二个回波应被视为第一个回波的参数。

在bash中,它非常简单,因为{ cmd; }应该有空格和分号。但是,例如在zsh中并不需要它们,但是仍然可以通过分析{}shell 上下文来判断应使用其内容做什么。

考虑以下:

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

两者都返回当前日期,但是

echo {1..3}

返回值,1 2 3因为shell知道{}command的参数echo,因此应将其扩展。


{其次是未加引号的空格不会在bash中开始大括号扩展。
choroba

@choroba是的,不仅在之后{。未加引号的空间不能在任何地方,因为Shell会将整个命令行分割为空格。
jimmij

0

首先,复合花括号本身必须是一个单词,也是命令行的第一个单词:

echo { these braces are just words }

其次,单个括号并不特殊(如上所示)。空括号也不是特别的:

echo {} # just the token {}: familiar from the find command

没有逗号的任何东西本身也就是

echo {abc} # just {abc}

这是动作开始的地方。

echo {a,b} # braces disappear, a b results.

因此,基本上,为了使括号扩展生效,我们需要一个单词(在空格中不分隔成多个字段),在其中至少出现一个实例,{...}在其中至少出现一个逗号。

顺便说一句,这可以是命令行中的第一个单词:

{ls,-l} .   # just "ls -l ."
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.