Bash扩展

Bash支持如下扩展:大括号扩展、波浪号扩展、参数和变量扩展、算术扩展、命令替换、单词拆分、文件名扩展。它们按照顺序依次进行扩展。

大括号扩展具有如下样式:preamble{str,str,str|start..end..[..incr]}postscript 一个前缀,大括号中的一系列逗号分隔的字符串或者一个序列表达式,以及一个后缀。前后缀与括号中的每个项目分别匹配,大括号扩展是可以嵌套的。举例如下:

1
2
3
4
5
echo a{d,c,b}e  => abe ace ade
echo A{0..9}B   => A0B A1B A2B A3B A4B A5B A6B A7B A8B A9B
echo {a..f}         => a b c d e f
cp file22.{txt,backup}  # Copies file22.txt to file22.backup
for i in {0..9} ; do echo $i done => 0 1 2 3 4 5 6 7 8 9

序列表达式的start和end可以都是整数或者单个字符,可选增量是一个整数,序列包括头尾。如果start和end中任何一个是整数并且以0开头,那么产生的数字将有同样的宽度,不够用前导0补齐(此特性仅在bash中有效,sh无效)。

在Shell的所有扩展中大括号扩展(brace expansion)是第一实施的,执行的严格的文本替换,特别是不进行变量扩展。因为像 {0..$END} 这种语句不会执行期望的扩展。${一定不会被当做大括号扩展。括号中出现的{``,需要用反斜杠来转义。

括号中不允许有任何空格,除非被引用或转义。去掉一下语句中任何一个转义符看看结果:

1
2
echo {file1,file2}\ :{\ A," B",' C'}
echo \"{These,words,are,quoted}\"

波浪号扩展(tilde expansion):如果一个单词以~头,那么~直到/被称为波浪前缀(tilde-prefix)。

1
2
3
4
~            => $HOME
~/foo        => $HOME/foo
~fred/foo    => fred的home目录下的foo子目录
~+/foo       => $PWD/foo

变量扩展(shell parameter expansion):变量扩展以$字符后接变量名,可以在变量名两边包裹大括号,形如${var},只有当变量名为位置变量(postional parameter)并且大于等于10时大括号是必须的,或者后面紧接着别的字符时也是必须的。变量扩展中${!var}形式称为间接变量(indirect parameter),其中var的值作为新的变量名,再取新变量的值。

以下形式的变量扩展,其中 word 可以是波浪扩展、变量扩展、命令替换以及算术扩展表达式。如果有冒号,将同时检查 parameter 是否存在以及是否为空串,如果没有冒号,将只检查 paramter 是否存在。(在bash中空串与null含义相同)。

  • ${parameter:-word} 如果parameter不存在或为null,取word的值,否则取parameter本身的值;
  • ${parameter:=word} 如果parameter不存在或为null,word的值赋给parameter,然后取其值,否则取parameter本身的值;
  • ${parameter:?word} 如果parameter不存在或为null,word值被写入到标准误,然后退出脚本,否则取parameter本身的值;
  • ${parameter:+word} 如果parameter不存在或为null,不做任何替换,否则取word的值;
  • ${parameter:offset} ${parameter:offset:length} 被扩展为选取parameter字符串中从offset开始,length长度的子字符串,也被称为子字符串扩展(substring expansion)。如果parameter是位置变量或者数组,length为元素的个数,offset为元素起始位置。在三种情况下,offset如果是负数表示从尾部计算,length如果为负数表示结束点从尾部计算的位置。(offset和length支持负数的特性仅在bash中有效);
  • ${!prefix*} ${!prefix@} 扩展为以prefix为前缀的变量名列表;
  • ${#parameter} 扩展为变量值的长度,如果parameter是*或者@则为位置变量的数目,如果是数组则为数组的元素个数;
1
2
${#*} ${#@}                         #位置参数的个数
${#array[*]} ${#array[@]}     #数组的元素的个数
  • ${parameter#word} ${parameter##word} 从parameter的头部删除掉匹配word的子字符串,返回剩余部分,#匹配最短,##匹配最长。如果parameter是*或者@或数组,则对里边的每个变量应用一次;
1
2
3
4
parameter=/var/share/my.file.txt
echo ${parameter##*/}   #去掉从开头到最后一个/的所有字符:my.file.txt
echo ${parameter#*.}     #去掉从开头到第一个.的所有字符:file.txt
echo ${parameter##*.}   #去掉从开头到最后一个.的所有字符:txt
  • ${parameter%word} ${parameter%%word} 与#方式反过来,匹配尾部,并删除字符串,返回剩余部分。%匹配最短,%%匹配最长。如果parameter是*或者@或数组,则对里边的每个变量应用一次;
1
echo ${parameter%/*}   #去掉尾部的/及其之后的字符:/var/share
  • ${parameter/pattern/string} 从parameter值中匹配pattern并替换为string,默认只替换第一个,如果pattern以/开始则替换所有,如果pattern以#开头,则必须匹配parameter的开头部分,如果pattern以%则必须比配parameter的结尾部分。如果没有string部分,则执行匹配删除。如果parameter是*或者@或数组,则对里边的每个变量应用一次;
  • ${parameter^pattern} ${parameter^^pattern} ${parameter,pattern} ${parameter,,pattern} 对parameter的值执行匹配并更换大小写。匹配原则是parameter中的每个字符与pattern进行匹配,因而,pattern必须是单个字符的模式。^将首字母小写转为大写,^^将全部小写转大写,,将首字母大写转为小写,,,将全部大写转小写。如果省略pattern,则匹配整个字符串。如果parameter是*或者@或数组,则对里边的每个变量应用一次;

命令替换(command substitution):将命令的输出替换命令本身。形式:$(command)`command`。推荐使用新式的 $() 形式,因为旧式的反引号形式不容易嵌套命令,并且对 \ ' $ 需要特殊处理。$(cat file)$(<file)结果一样。

算术扩展(arithmetic expansion)引用bash中出现算术表达式,形如:$(( expression )) 算术扩展的变量可以是变量扩展、命令扩展的结果。

单词拆分(word splitting):对于没有在引号中的字符串先取出头尾的空白符再进行单词拆分。单词拆分以 $IFS 中的每个字符进行分割,默认 IFS<space><tab><newline>,改变 IFS 的值会改变单词分割的方式,特别是当 IFS 为空字符串时不进行单词拆分。显式的空字符串 "" '' 会被保留,但是没有引号的空字符串会被忽略。

文件名扩展(filename expansion)是Bash扩展中的最后一个,当非引号中的字符串含有 * ? [ 时就以此为模式进行文件名匹配,返回排好序的文件名列表;在文件名扩展中,. / 两个字符不是特殊字符。

  • * 匹配所有字符串包括空字符串;
  • ? 匹配单个字符;
  • [...] 匹配单个字符,跟常用的正则一样的,要匹配 - ] ^时,] 放在头部,^ - 放在尾部,还有一种 [:class:] 的形式来指定一族字符;
    • [:alnum:] 匹配所有字母和数字字符,相当于 A-Za-z0-9
    • [:alpha:] 匹配所有字母,相当于 A-Za-z
    • [:ascii:] 匹配所有ASCII字符
    • [:blank:] 匹配空格和tab
    • [:digit:] 匹配所有数字 0-9
    • [:word:] 匹配所有字母数字下划线 [A-Za-z0-9_]
    • [:punct:] 匹配所有标点符号
    • [:cntrl:] 匹配控制字符(ASCII 0-31) [\x00-\x1F\x7F]
    • [:graph:] 匹配可打印字符(ASCII 32-126),剔除空格
    • [:lower:] 匹配小写字母a-z
    • [:upper:] 匹配大写字母A-Z
    • [:print:] 匹配可打印字符,包括空格
    • [:space:] 匹配所有空白符,包括换行符
    • [:xdigit:] 匹配十六进制数字 0-9A-Fa-f

以上的 character classes 必须放在中括号中

参考阅读:

  1. POSIX Bracket Expressions
  2. BOOK: Bash Reference Manual
  3. BOOK: BASH 中文文档
  4. BOOK: Advanced Bash-Scripting Guide