Bash 手册 v3.2 - 5

3.5 Shell扩展
=============
                                                                                                                            
    命令行在被分割成一个个'token'后, 将进行一系列扩展. 有以下几种类型的
扩展:
                                                                                                                           
    * 大括号扩展
    * 波浪号扩展
    * 参数和变量扩展
    * 命令替换
    * 算术扩展
    * 单词分割
    * 文件名扩展
                                                                                                                           
    扩展的顺序是: 大括号扩展, 波浪号扩展, 参数, 变量, 算术扩展和命令替换
(自左向右进行), 单词分割, 文件名扩展.
                                                                                                                           
    在某些系统中, 还存在另一种扩展: 进程替换. 它在参数, 变量, 算术扩展和
命令替换的同时进行扩展.
                                                                                                                           
    只有大括号扩展, 单词分割, 以及文件名扩展能改变扩展单词的数目; 其他扩展
都是把一个单词扩展为一个单词. 唯一的例外是"$@"的扩展(*参见 3.4.2 特殊参数::)
和"${NAME[@]}"的扩展(*参见 6.7 数组::).
                                                                                                                           
    在所有的扩展完成之后, shell会进行'引用去除'操作(*参见 3.5.9 引用去除::).
                                                                                                                           
3.5.1 大括号扩展
----------------
                                                                                                                          
    大括号扩展是一种可以产生任意多字符串的机制. 这种机制有点类似于文件名扩展
(*参见 3.5.8 文件名扩展::), 但其产生的文件名可以不存在. 用以大括号扩展的模式
通常采用这种形式构成: 一个可选的前缀, 后跟用大括号包围的以逗号分隔的字符串
列表, 或者用大括号包围的一个序列表达式, 后跟一个可选的后缀. 在扩展时, 前缀
被加在大括号内每个字符串前, 然后后缀被附加到上面产生的每个字符串后, 从左向
右扩展出多个字符串.
                                                                                                                          
    大括号扩展可以嵌套. 扩展所得的字符串不会被排序: 固定采用从左到右的顺序.
例如:
    bash$ echo a{d,c,b}e
    ade ace, abe
                                                                                                                          
    形如'{X..Y}'的表达式称作序列表达式, 其中X和Y是整数或者字符. 当使用整数
时, 表达式被扩展为介于X和Y之间的一个个数字, 包括X和Y. 当使用字符时, 表达式
被扩展为按字母集规定介于X和Y之间的一个个字符, 包括X和Y. 注意, X和Y必须为同
一种类型.
                                                                                                                          
    大括号扩展先于其它任何扩展, 并且, 对其它任何扩展有特殊意义的字符对大括号
扩展来说都是无特殊含义的. 也就是说, 大括号扩展是纯文本的, Bash不对扩展的内容
或介于两个大括号之间的文本采用任何语法解释. 所以为了避免和参数扩展产生混淆,
字符串'${'最好不要用于大括号扩展.
                                                                                                                          
    格式正确的大括号扩展必须包含未引用的开始和结束大括号, 并且还要有至少一个
未引用的逗号, 或一个正确的序列表达式. 任何错误的格式都会使大括号扩展不做任何
扩展并保持不变.
                                                                                                                          
    如果要使用字符'{'或','本身参与扩展, 要在它们前面加一个反斜杠来防止被解释
为大括号扩展的一部分. 为了避免和参数扩展产生混淆, 字符串'${'最好不要用于大括
号扩展.
                                                                                                                          
    一般当要生成的字符串的公共前缀比上例中长的时候, 会使用大括号扩展作为一种
便捷方式, 如:
    mkdir /usr/local/src/bash/{old,new,dist,bugs}
或:
    chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
                                                                                                                          
3.5.2 波浪号扩展
----------------

    如果一个单词以一个未引用的波浪号('~')开头, 那么该波浪号以及其后一直到
第一个未引用的反斜杠前的所有字符(如果没有未引用的反斜杠, 则所有字符)被称为
是一个'波浪号前缀'. 如果波浪号前缀中没有字符被引用, 那么波浪号之后的字符被
认为可能是一个'登录名'. 如果该登录名是一个null串, 波浪号将被shell变量HOME的
值替换. 如果HOME未设置, 就用当前执行shell的用户的家目录替换波浪号. 除此外,
波浪号前缀被所指定的登录名的家目录替换.

    如果波浪号前缀是'~+', 就用shell变量PWD的值替换波浪号前缀. 如果波浪号前缀
是'~-', 就用shell变量OLDPWD的值(如果此值已设置)替换波浪号前缀.

    如果波浪号后面是一个数字N, 并且在N前还有一个可选的字符'+'或'-', 那么用
目录栈里面对应的值来替换波浪号前缀. 以波浪号前缀里波浪号后的字符串为参数, 调用
内部命令dirs, 就可以得到该值(*参见 6.8 目录栈::). 如果波浪号后只是一个数字,
没有'+'或'-', 那么当作有'+'对待.

    如果登录名不正确, 或者其他原因导致波浪号扩展失败, 则该单词保留不变.  

    在变量赋值语句中, shell也会检查在':'或第一个'='之后有没有未引用的波浪号
前缀. 如果有, 也会进行波浪号扩展. 所以, 用户可以把波浪号和文件名赋给PATH,
MAILPATH, 以及CDPATH, 这种情况下, shell赋的是扩展后的值.

    下表展示了Bash是如何处理未引用的波浪号前缀的:
~            $HOME
~/foo        $HOME/foo
~fred/foo    用户fred家目录下的子目录或文件foo
~+/foo        $PWD/foo
~-/foo        ${OLDPWD-'~-'}/foo
~N            执行命令'dirs +N'所显示的串
~+N            执行命令'dirs +N'所显示的串
~-N            执行命令'dirs -N'所显示的串

3.5.3 Shell参数扩展
-------------------

    字符'$'引入了参数扩展, 命令替换, 和算术扩展. 参与扩展的参数名或符号可以
放在大括号内, 大括号是可选的, 它的作用是将要扩展的变量和变量之后可能被认为是
变量一部分的字符界定开来.
    当使用大括号时, 匹配的结束大括号是第一个未被反斜杠引去且未被引号引用的'}',
并且它不在嵌套的算术扩展, 命令替换或参数扩展内.
    参数扩展的基本形式是${parameter}, 其结果是用parameter的值替换${parameter}.
当parameter是一个多于一位数字的位置参数时, 或parameter后面跟着不想被视为
parameter一部分的其他字符时, 大括号是必须的.
    如果parameter的第一个字符是感叹号, 则导致变量被间接化. 这时, 用来扩展的
变量不是parameter自己, 而是'!'之后部分所形成的变量的值. Bash视该值为变量对其
进行扩展并用以替换${parameter}. 这种情况叫做'间接扩展'. 对此, 唯一的例外是
${!prefix*}和${!name[@]}的扩展, 下面会详细结束. 注意要间接扩展, 必须在左大括号
之后紧接着一个感叹号.
    在下面的例子中, 'word'上可以运用波浪号扩展, 参数扩展, 命令替换和算术扩展.
    在不是子串扩展的情况下, 如果扩展形式中有冒号, 则Bash要测试参数是否设置以及
是否为null; 如果扩展形式中没有冒号, 那么Bash仅测试参数时候设置(是否存在).

${parameter:-word}
    如果parameter没有设置(不存在)或值为null, 用word的扩展结果替换; 否则, 用
    parameter的值替换.

${parameter:=word}
    如果parameter没有设置(不存在)或值为null, 先将word的扩展结果赋给parameter,
    然后用parameter的值替换. 位置参数和特殊参数不能用这种方法赋值.

${parameter:?word}
    如果parameter没有设置(不存在)或值为null, 将word的扩展结果(如果word不存在,
    则是一条相同效果的消息)写到标准错误, 并且如果当前的shell是非交互式的, 则
    shell退出; 如果不是以上的情况, 用parameter的值替换.

${parameter:+word}
    如果parameter没有设置(不存在)或值为null, 不做任何事; 否则用word的扩展结果
    替换.

${parameter:offset}
${parameter:offset:length}
    扩展为parameter中从offset开始的最长为length个字符的串. 如果没有指定length,
    则扩展为parameter的从offset指定字符开始的子串. length和offset都是算术
    表达式(*参见 6.5 Shell算术::). 这种扩展叫做子串扩展.
    length必须被求值为一个大于等于零的整数. 如果offset求值小于零, 则被视作从
    parameter扩展结果尾部开始的偏移. 如果parameter是@, 则扩展结果是从offset
    开始的length个位置参数. 如果parameter是一个下标为@或*的数组名, 则扩展结果
    是该数组中以${paramter[offset]}开始的length个元素, 这时如果offset为负则
    视为从数组最大索引加1开始. 注意如果使用负的offset, 必须在冒号和负号之间
    加至少一个空白符, 以避免和':-'扩展混淆. 子串扩展都是从0开始索引的, 唯一
    的例外是位置参数, 它们从1开始索引.
  
${!prefix*}
${!prefix@}
    扩展为以prefix开头的变量名, 这些名字用特殊变量IFS的第一个字符分隔.  

${!name[@]}
${!name[*]}
    如果name是一个数组变量, 则扩展为该数组所使用的索引下标(键)的列表; 如果
    name不是数组变量且设置了(存在), 则扩展为0; 如果name不是数组变量且没有
    设置(不存在), 则扩展为null. 当使用'@'并且扩展被双引用时, 每一个键被扩展
    为一个独立的单词.

${#parameter}
    用parameter扩展后的值所包含的字符数替换. 如果parameter是'*'或'@', 则用
    位置参数的数目替换. 如果parameter是一个下标为'*'或'@'的数组名, 则用该
    数组的元素个数替换.

${parameter#word}
${parameter##word}
    首先word依照文件名扩展的规则(*参见 3.5.8 文件名扩展::)被扩展为一个模式.
    如果该模式匹配parameter扩展值的头部, 则最终扩展结果为parameter扩展值去除
    pattern的最短匹配(使用#时)或最长匹配(使用##时)后剩下的部分. 如果parameter
    是'@'或'*', 则依次对每一个位置参数做上述去除操作, 最终扩展结果为操作后的
    位置参数的列表. 如果parameter是一个下标为'@'或'*'的数组变量, 则依次对每
    一个数组元素做上述去除操作, 最终扩展结果为操作后的数组元素的列表.

${parameter%word}
${parameter%%word}
    首先word依照文件名扩展的规则(*参见 3.5.8 文件名扩展::)被扩展为一个模式.
    如果该模式匹配parameter扩展值的尾部, 则最终扩展结果为parameter扩展值去除
    pattern的最短匹配(使用%时)或最长匹配(使用%%时)后剩下的部分. 如果parameter
    是'@'或'*', 则依次对每一个位置参数做上述去除操作, 最终扩展结果为操作后的
    位置参数的列表. 如果parameter是一个下标为'@'或'*'的数组变量, 则依次对每
    一个数组元素做上述去除操作, 最终扩展结果为操作后的数组元素的列表.

${parameter/pattern/string}
    首先pattern依照文件名扩展的规则(*参见 3.5.8 文件名扩展::)被扩展为一个模式.
    然后parameter被扩展并且其中与pattern匹配的最长部分被替换为string. 通常只有
    只有第一个这样的匹配被替换为string, 如果pattern以'/'开头则所有pattern的
    匹配都被替换为string. 如果pattern以'#'开头, 它只去寻找parameter扩展值头部
    的匹配; 如果pattern以'%'开头, 它只去寻找parameter扩展值尾部的匹配. 如果
    string为null, pattern的匹配会被删除并且这时pattern后的'/'可以省略. 如果
    parameter是'@'或'*', 则依次对每一个位置参数做上述替换操作, 最终扩展结果为
    操作后的位置参数的列表. 如果parameter是一个下标为'@'或'*'的数组变量, 则
    依次对每一个数组元素做上述替换操作, 最终扩展结果为操作后的数组元素的列表.


3.5.4 命令替换
--------------

    命令替换机制允许用命令的输出替换命令自身. 当命令被如下方式包裹时将发生
命令替换:
        $(command)

        `command`

    Bash处理这种扩展的做法是: 执行command然后用command的标准输出来替换`command`
或$(command), 最后再删除所有的结尾新行符. 嵌入在输出内部的新行符不会被命令替换
删除, 但它们有可能在单词分割的时候被去除. 命令替换$(cat file)可以用等价但更快
的方式$(< file)来代替.

    当使用旧式的反引号型命令替换时, 反斜杠保持其字面意义, 但在置于'$', '''和
'\'之前时例外. 第一个没有前置反斜杠的反引号将结束命令替换. 当使用$(command)
形式的命令替换时, 小括号之间的所有字符组成了命令, 它们都不会被特殊对待.

    命令替换可以嵌套, 当使用反引号型嵌套时, 需要在嵌套反引号前加上反斜杠.

    如果命令替换出现在双引号内, 扩展结果不会进行单词分割和文件名扩展.


3.5.5 算术扩展
--------------

    算术扩展对算术表达式求值并用其结果来替换. 算术扩展的格式为:
        $(( expression ))

    该表达式被当作好像置于双引号内一样对待, 但小括号内的双引号不再被特殊对待.
表达式内的所有token须经过参数扩展, 命令替换, 和引用去除. 算术扩展可以嵌套.
  
    对表达式的求值将根据下面要列出的规则(*参见 6.5 Shell算术::). 如果表达式
不正确, Bash会向标准错误打印一条消息提示失败, 并且不会再做替换.

你可能感兴趣的:(职场,休闲)