引号:
Shell首先会根据IFS变量把命令行中未被引号引入的部分拆分成一个个word(IFS默认为空格、制表符、换行)。若空格被\转义,则会被视为一个word中的一部分。
双引号中可以随意包含单引号,单引号中也可以随意包含双引号。但是双引号要被转义之后才能被双引号包含。而单引号中的任何字符都不会被视为特殊字符,因此单引号中没办法再包含单引号了,即使被转义。一个转义的单引号也会提前结束前面的那个单引号。若需要在单引号中包含单引号,即正确转义,则需借助语法:
echo $'hello \'world\'' # hello 'world'
一、大括号扩展(非 POSIX 标准)
未被引号引起来的大括号中,若有以逗号(,)连接的列表,或者用双点号(..)指定的范围,则会被扩展为独立的参数(逗号和双点前后都不能有空格,且逗号和双点不能同时使用):
echo {1,2,3} # 1 2 3
echo {a..c} # a b c
该扩展可以被嵌套:
echo {{1,2,3},{a..c}} # 1 2 3 a b c
该扩展还可以产生迪卡尔集:
echo g{e,o}t
# get
# got
echo a{1..3}
# a1
# a2
# a3
echo {a..c}{1..2}
# a1
# a2
# b1
# b2
# c1
# c2
二、波浪号扩展
未被引号引起来的波浪号会被扩展为用户的主目录。波浪号后还可以跟一个合法的用户名以指定用户。若用户名不合法则不被扩展:
echo ~ # 会扩展
echo ~root # 会扩展
echo ~invalid # 不会扩展
三、变量扩展
变量扩展使用$
符号将一个变量扩展为它的内容:
a=abc
echo $a # abc
echo ${a} # abc
echo $a_d # abc_d
未被双引号引起来的变量扩展,结果还会经历单词分割以及路径扩展。
四、算术扩展
Bash遇到$(())
表达式时,会以算术运算来计算该表达式:
echo $(( 1 + 2 * 3 )) # 7
未被双引号引起来的算术扩展,结果还会经历单词分割。
五、命令扩展
命令扩展即将命令用它执行的输出来替换:
echo `date +%C` # 20
echo $(date +%C) # 20
未被双引号引起来的命令扩展,结果还会经历单词分割和路径扩展。
六、单词分割
未被引号引起来的序列都会进行单词分割。单词分割基于IFS变量。IFS的默认值是$' \t\n'
。
当IFS是默认值或者是unset时,默认IFS中的任何序列都被视为一个单独的分隔符;当IFS包含空白和非空白符时,多个连着的空白符仍被视为一次分隔,然而每个非空白符都被视为一次分隔;当IFS只包含非空白符时,则任何该非空白符以及任何空白符都会视为单独的。
七、路径扩展
未被引号引起来的*
, ?
, [
都会被视为文件匹配符,并且在有文件能正确匹配的前提下,它们会被扩展为匹配到的文件的列表。其中,*
匹配任意字符;?
匹配某一个字符;[
以[aeiou]
, [a-zA-Z]
, [[:lower:]]
的形式进行正则匹配。
八、进程替换
进程替换为进程创建用于重定向IO的临时文件名。它们可以出现在任何需要一个文件名的地方。<(command)
表示command输出的文件名;>(command)
是command输入的文件名。
进程替换优于|
管道的一个理由是不会把一些需要的变量带到子进程中。
九、其他
Bash从Csh继承了!
的使用,用于查找历史命令并执行,并且该扩展不能够被双引号取消,只能被单引号取消。比如:
!! # 执行上一条执行的命令
"!!" # 同上
'!!' # 出错
!x # 执行之前最后一条以x开头的命令
"!x" # 同上
'!x' # 出错
但是这也引出了一个问题,即在bash上想显示”hello!”这个字符串,直接这样写是不行的:
echo "hello!" # !" 要么被扩展成上一条以 " 开头的命令,要么出错
简单的解决办法有两个:
echo 'hello!' # 以单引号代替双引号
echo "hello! " # 在 ! 后加上一个空格,避免 ! 被扩展