3.4.1. 总结

在命令被拆分为词根(参见第 1.4.1.1 节)之后,这些标记或单词被扩展或解析。执行的扩展有八种,我们将在下一节中讨论它们的扩展顺序。

在所有扩展之后,执行引号删除。

3.4.2. 大括号扩展

大括号扩展是一种可以生成任意字符串的机制。要进行大括号扩展的模式采用可选PREAMBLE的形式,后跟一对大括号之间的一系列逗号分隔字符串,然后是可选的POSTSCRIPT。前缀添加到大括号中包含的每个字符串,然后将后记附加到每个结果字符串,从左到右扩展。

大括号扩展可能是嵌套的。每个展开字符串的结果不排序;从左到右的顺序被保留:

  1. franky ~> echo sp{el,il,al}l
  2. spell spill spall

大括号扩展在任何其他扩展之前执行,并且任何其他扩展的特殊字符都保留在结果中。它是严格的文本。Bash 不对扩展的上下文或大括号之间的文本应用任何句法解释。为避免与参数扩展发生冲突,字符串“${”不适合大括号扩展。

格式正确的大括号展开必须包含不带引号的左大括号和右大括号,以及至少一个不带引号的逗号。任何格式不正确的大括号扩展都无效。

3.4.3. 波浪号扩展

如果单词以不带引号的波浪号字符 ( “~” ) 开头,则直到第一个不带引号的斜杠(或所有字符,如果没有不带引号的斜杠)之前的所有字符都被视为tilde-prefix。如果波浪号前缀中没有任何字符被引用,波浪号后面的波浪号前缀中的字符将被视为可能的登录名。如果此登录名是空字符串,波浪号将替换为HOME shell 变量的值。如果HOME未设置,则替换执行 shell 的用户的主目录。否则,波浪号前缀将替换为与指定登录名关联的主目录。

如果tilde-prefix是”~+”,shell 变量PWD的值将替换tilde-prefix。如果tilde-prefix为”~-“,则替换 shell 变量OLDPWD的值(如果已设置)。

如果tilde-prefix中波浪号后面的字符由数字 N 组成,可选前缀为“+”或“-”,tilde-prefix将替换为目录堆栈中的相应元素,因为它将显示由dirs调用,其中tilde-prefix中波浪号后面的字符作为参数。如果tilde-prefix(没有波浪号)由一个没有前导“+”或“-”的数字组成,则假定为“+” 。

如果登录名无效,或者波浪号扩展失败,则单词保持不变。

每个变量赋值都会检查紧跟在”:”或”=”之后的未加引号的tilde-prefix。在这些情况下,也执行波浪号扩展。因此,可以在分配给PATH、MAILPATH和CDPATH时使用带有波浪号的文件名,并且 shell 分配扩展值。

  1. franky ~> export PATH="$PATH:~/testdir"

~/testdir将扩展为$HOME /testdir,因此如果$HOME是/var/home/franky,目录/var/home/franky/testdir将添加到PATH变量的内容中。

3.4.4. Shell参数和变量扩展

“ $”字符引入了参数扩展、命令替换或算术扩展。要扩展的参数名称或符号可以用大括号括起来,用于保护要扩展的变量免受紧随其后的字符的影响。

当使用大括号时,匹配的结束大括号第一个“}”,它没有被反斜杠转义,也没有在带引号的字符串中,也没有在嵌入的算术扩展、命令替换或参数扩展中。

参数扩展的基本形式是”${PARAMETER}”。“PARAMETER”的值被替换。当“PARAMETER”是一个多于一位的位置参数时,或者当“PARAMETER”后跟一个不被解释为其名称的一部分的字符时,大括号是必需的。

如果“PARAMETER”的第一个字符是感叹号,Bash 使用由“PARAMETER”的其余部分组成的变量的值作为变量的名称;然后扩展此变量,并将该值用于其余的替换,而不是“PARAMETER”本身的值。这称为间接扩展。(就是会去模糊匹配命令)比如 ${!N},${!R}

您熟悉直接参数扩展,因为它一直在发生,即使在最简单的情况下,例如上面或下面的情况:

  1. franky ~> echo $SHELL
  2. /bin/bash

The following is an example of indirect expansion:

  1. franky ~> echo ${!N*}
  2. NNTPPORT NNTPSERVER NPX\_PLUGIN\_PATH

请注意,这与echo $N*不同。

如果变量尚不存在,则以下构造允许创建命名变量:

${VAR:=value}

例子:

  1. franky ~> **echo $FRANKY
  2. franky ~> **echo ${FRANKY:=Franky}
  3. Franky

然而,特殊参数,尤其是位置参数,不能以这种方式分配。

3.4.5. 命令替换

命令替换允许命令的输出替换命令本身。当像这样包含命令时会发生命令替换:

$(command)

或者像这样使用反引号:

command

Bash 通过执行 COMMAND 并将命令替换替换为命令的标准输出来执行扩展,并删除任何尾随的换行符。嵌入的换行符不会被删除,但它们可能会在分词过程中被删除。

  1. franky ~> echo `date`
  2. Thu Feb 6 10:06:20 CET 2003

当使用旧式反引号形式的替换时,反斜杠保留其字面含义,除非后面跟着”$”、”`”或”\”。前面没有反斜杠的第一个反引号终止命令替换。使用“$(COMMAND)”形式时,括号之间的所有字符组成命令;没有特殊对待。

命令替换可能是嵌套的。要在使用反引号形式时嵌套,请使用反斜杠转义内部反引号。

如果替换出现在双引号中,则不会对结果执行分词和文件名扩展。

3.4.6. 算术扩展

算术扩展允许对算术表达式求值并替换结果。算术展开的格式为:

$((EXPRESSION))

表达式被视为在双引号内,但括号内的双引号不会被特殊处理。表达式中的所有标记都经过参数扩展、命令替换和引号删除。算术替换可以嵌套。

算术表达式的计算是在固定宽度的整数中完成的,不检查溢出 - 尽管被零除被捕获并被识别为错误。运算符与 C 编程语言中的运算符大致相同。按照优先级递减的顺序,列表如下所示:

Table 3-4. Arithmetic operators

Operator Meaning
VAR++ and VAR— 可变后增量和后减量
++VAR and —VAR 可变预增和预减
- and + 一元减号和加号
! and ~ 否定
** 求幂
*, / and % 乘法、除法、余数
+ and -
<> 左右位移
<=,>=, < and > 比较运算符
== and != 等与不等
&
^
|
&&
||
expr ? expr : expr 三元表达式
=, *=, /=, %=, +=, -=, <<=,>>=, &=, ^= and |=
, 表达式之间的分隔符

shell 变量允许作为操作数;在计算表达式之前执行参数扩展。在表达式中,shell 变量也可以通过名称来引用,而无需使用参数扩展语法。变量的值在被引用时被计算为算术表达式。shell 变量不需要其整数属性才在表达式中使用。

带有前导 0(零)的常量被解释为八进制数。前导“0x”或“0X”表示十六进制。否则,数字采用”[BASE’#’]N”形式,其中”BASE”是 2 到 64 之间的十进制数,表示算术基数,N 是该基数中的数字。如果省略“BASE’#’”,则使用基数 10。大于 9 的数字依次由小写字母、大写字母、“@”和“_”表示。如果“BASE”小于或等于 36,

运算符按优先级顺序计算。括号中的子表达式首先被评估。

Bash 用户应尽可能尝试使用带方括号的语法:

$[ EXPRESSION ]

但是,这只会计算EXPRESSION的结果,不做任何测试:

  1. franky ~> echo $[365*24]
  2. 8760

3.4.7. 进程替换

进程替换支持命名管道 (FIFO) 或/dev/fd方法打开的具名文件。它采取以下形式

<(LIST)

或者

>(LIST)

进程LIST运行时其输入或输出连接到 FIFO 或/dev/fd中的某个文件。作为扩展的结果,此文件的名称作为参数传递给当前命令。如果使用”>(LIST)”形式,写入文件将为LIST提供输入。如果使用“<(LIST)”形式,则应读取作为参数传递的文件以获得LIST的输出。请注意,< 或 > 符号和左括号之间不能出现空格,否则该构造将被解释为重定向。

3.4.8. 分词

shell 会扫描双引号内未出现的参数扩展、命令替换和算术扩展的结果以进行分词。

shell 将$IFS的每个字符视为分隔符,并将其他扩展的结果拆分为这些字符上的单词。如果IFS未设置,或者它的值恰好是默认值”‘‘“,那么任何IFS字符序列都用于分隔单词。如果IFS的值不是默认值,则在单词的开头和结尾处忽略空格字符“space”和“Tab”的序列,只要空格字符在 IFS 的值中(一个IFS空格特点)。空白与任何相邻的IF空白字符一起分隔一个字段。IFS空白字符序列也被视为分隔符。如果IFS的值为空,则不发生分词。

保留显式空参数(””””或”‘’” )。不带引号的隐式空参数由没有值的参数的扩展产生。如果在双引号内扩展了没有值的参数,则会产生并保留空参数。

3.4Shell扩展 - 图1
扩展和分词

如果没有发生扩展,则不执行拆分。

3.4.9. 文件名扩展

分词后,除非设置了-f选项(参见第 2.3.2 节),否则 Bash 会扫描每个单词中的字符”*”、”?” , 和”[“。如果出现这些字符之一,则该单词被视为PATTERN,并替换为按字母顺序排列的与该模式匹配的文件名列表。如果没有找到匹配的文件名,并且 shell 选项nullglob被禁用,则单词保持不变。如果设置了nullglob选项,但没有找到匹配项,则删除该单词。如果启用了 shell 选项nocaseglob,则执行匹配时不考虑字母字符的大小写。

当模式用于文件名生成时,字符“.” 除非设置了 shell 选项dotglob,否则必须显式匹配文件名的开头或紧跟在斜杠之后的位置。匹配文件名时,斜杠字符必须始终显式匹配。在其他情况下,“。” 字符没有特殊处理。

GLOBIGNORE shell变量可用于限制匹配模式的文件名集。如果设置了 GLOBIGNORE,则从匹配列表中删除每个也匹配GLOBIGNORE中的模式之一的匹配文件名。文件名. 和..总是被忽略,即使设置了 GLOBIGNORE。但是,设置GLOBIGNORE具有启用dotglob shell 选项的效果,因此所有其他以“.”开头的文件名 会匹配。

https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html