1 扩展(expansion)
1.1 什么是扩展
每次键入命令然后按下enter键这一过程中,bash在执行命令前会对文本进行处理。我们已经见到过了简单的字符序列对于shell有很多意义,例如 *符号。对文本处理的过程称为扩展(expansion)。通过扩展,可以输入内容,然后在shell对其执行之前将其扩展为其他内容。
1.2 echo 命令
我们将用 echo 命令来展示扩展(explanation)
echo—Display a line of text.
[me@linuxbox ~]$ echo this is a test
this is a test
上面的例子中 echo 命令只是输出后面的参数,如果改变后面的参数为符号 * 会发生什么
[me@linuxbox ~]$ echo *
Desktop Documents ls-output.txt Music Pictures Public Templates Video
上面例子中为什么没有输出字符* 呢?这是因为在通配符中*代表着“输出任何字符的文件名”,shell是如何做到这一点的呢?
简单答案是在echo命令执行之前,shell扩展* (在这个例子中为所在目录下的文件名称)。当按下回车键,shell在执行命令前自动的扩展(expand),因此传入 echo 这个命令的参数实际上不是 *,而是扩展后的结果。
2 扩展命令
2.1 文件名扩展(filename expansion)
我们可以使用通配符来实现文件名扩展。
[me@linuxbox ~]$ ls
Desktop ls-output.txt Pictures TemplatesDocuments Music Public Video
[me@linuxbox ~]$ echo D*
Desktop Documents
[me@linuxbox ~]$ echo *s
Documents Pictures Templates Videos
[me@linuxbox ~]$ echo [[:upper:]]*
Desktop Documents Music Pictures Public Templates Videos
[me@linuxbox ~]$ echo /usr/*/share
/usr/kerberos/share /usr/local/share
关于隐藏文件的文件名扩展
在Linux系统中以小数点符号开始的文件名被隐藏。因此文件名扩展(filename expansion)也遵守这个规则。
想要输出隐藏文件,首先想到,在命令行输入命令 echo *输出全部文件,但是执行结果不包含隐藏的文件。
接着在星号前面加入小数点符号.来匹配隐藏文件,输入并执行
echo .*命令后,但是结果里也出现了代表当前目录与父目录的.以及..两个文件,这显然不是预期的结果。
接着分析隐藏文件名的特点,首字符为小数点,第二位字符应该是除了小数点的字符来匹配,因此使用ls -d .[!.]* 命令来扩展。
2.2 波浪号(~)扩展
~扩展为当前的用户目录,如果后面加上用户名就会扩展为此用户名的目录。
[me@linuxbox ~]$ echo ~
/home/me
[me@linuxbox ~]$ echo ~foo
/home/foo
2.3 算数扩展
shell允许算术扩展,这使得可以使用shell作为计算器,例如:
[me@linuxbox ~]$ echo $((2 + 2))
4
算数扩展使用以下的格式:
$((expression))
算数扩展只支持整数,以下是支持的几个运算符
| Operator | Description |
|---|---|
| + | Addition |
| - | Subtraction |
| * | Multiplication |
| / | Division (But remember, because expansion supports only integer arithmetic, results are integers.) |
| % | Modulo, which simply means remainder |
| ** | Exponentiation |
其中关于表达式嵌套的情况,我们也可以对其进行简写,例如:
[me@linuxbox ~]$ echo $(($((5**2)) * 3))
75
可以将上面的表达式重写为
[me@linuxbox ~]$ echo $(((5**2) * 3))
75
2.4 大括号扩展
大括号扩展可以包含前文(preamble)后记(postscript),大括号表达式包含了逗号分割的字符列表或者字符数字范围,此模式不能包含空格。
[me@linuxbox ~]$ echo Number_{1..5}
Number_1 Number_2 Number_3 Number_4 Number_5
倒叙:
[me@linuxbox ~]$ echo {Z..A}
Z Y X W V U T S R Q P O N M L K J I H G F E D C B
嵌套:
[me@linuxbox ~]$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b
应用场景: 假设有一堆照片,想要按照年月来把照片存储起来,首先要建立年月的文件夹:
[me@linuxbox ~]$ mkdir Pics
[me@linuxbox ~]$ cd Pics
[me@linuxbox Pics]$ mkdir {2009..2011}-0{1..9} {2009..2011}-{10..12}
[me@linuxbox Pics]$ ls
2009-01 2009-07 2010-01 2010-07 2011-01 2011-07
2009-02 2009-08 2010-02 2010-08 2011-02 2011-08
2009-03 2009-09 2010-03 2010-09 2011-03 2011-09
2009-04 2009-10 2010-04 2010-10 2011-04 2011-10
2009-05 2009-11 2010-05 2010-11 2011-05 2011-11
2009-06 2009-12 2010-06 2010-12 2011-06 2011-12
2.5 参数扩展(Parameter Expansion)
2.5.1 变量(variables)
在系统中,Linux存储小块数据并且分别给这些数据块起名字。这些数据块(更加准确地说这些变量(variables))可以对其进行参数扩展。例如:
[me@linuxbox ~]$ echo $USER
me
查看能用的变量列表:
[me@linuxbox ~]$ printenv | less
值得注意的是,在其他的扩展中如果对于模式有拼写错误,echo 命令会显示拼写错误的模式,但是在参数扩展中 echo 不会显示任何信息。
2.5.2 命令替换(Command Substitution)
命令替换 允许使用命令的执行结果作为扩展(expansion)
[me@linuxbox ~]$ echo $(ls)
Desktop Documents ls-output.txt Music Pictures Public Templates Videos
[root@izuf67yuy6secatlp4sztez ~]# ls $(which mkdir)
/usr/bin/mkdir
上面两个例子分别将命令ls以及which cp的执行结果作为参数内容传递到 分别传递到 echo 以及 ls 命令,因此不用知道它的路径就可以列出 mkdir 。
2.5.3 管道(pipelines )
[me@linuxbox ~]$ file $(ls /usr/bin/* | grep zip)
/usr/bin/bunzip2: symbolic link to `bzip2'/usr/bin/bzip2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped/usr/bin/bzip2recover: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped/usr/bin/funzip: ELF 32-bit LSB
...
上述例子使用管道后的执行结果成为file命令的参数列表,
使用命令替换还有另一种方式同时支持bash。可以用反引号`` 来代替美元$符号和括号()
[root@izuf67yuy6secatlp4sztez ~]# ls -l $(which mkdir)
-rwxr-xr-x. 1 root root 79768 Nov 6 2016 /usr/bin/mkdir
[root@izuf67yuy6secatlp4sztez ~]# ls -l `which mkdir`
-rwxr-xr-x. 1 root root 79768 Nov 6 2016 /usr/bin/mkdir
3 控制扩展
3.1 引用(Quoting)
现在知道了shell可以扩展,我们要学会如何控制扩展,比如,
[me@linuxbox ~]$ echo this is a test
this is a test
可以看到上面命令参数由于单词分割(word splitting)使得shell移除参数列表中额外的空格,再例如:
[me@linuxbox ~]$ echo The total is $100.00
The total is 00.0
参数扩展(parameter expansion)$1因为是未定义的变量因此输出空字符串。使得与预期输出不符合。
因此,shell提供引用(Quoting)来有选择的抑制不希望的扩展(expansions)。
3.2 双引号(Double Quotes)
如果将文本放在双括号中,shell将会把双括号中文本中所有的特殊字符将会失去它的特殊意义被当作正常的字符来处理。例外是美元符号$,反斜杠\,反引号``,这意味着字符分割(ord splitting),文件名扩展( pathname expansion, ),波浪号(tilde expansion)扩展以及大括号扩展被抑制,但是参数扩展( parameter expansion),算术扩展(arithmetic expansion),以及命令替换(command substitution )仍然起作用。
如果不加双引号,当文件名为two words.txt时:
[me@linuxbox ~]$ ls -l two words.txt
ls: cannot access two: No such file or directory
ls: cannot access words.txt: No such file or directory
使用双引号可以得到正确结果:
[me@linuxbox ~]$ ls -l "two words.txt"
-rw-rw-r-- 1 me me 18 2012-02-20 13:03 two words.txt
[me@linuxbox ~]$ echo this is a test
this is a test
默认情况下,分词(word splitting)查找空格、制表符和换行符(换行字符)的存在,并将它们视为单词之间的分隔符(delimiters)。这意味着不在引号 内的空格,制表符和换行符不被作为文本的一部分处理.
[me@linuxbox ~]$ echo $(cal)
February 2012 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[me@linuxbox ~]$ echo "$(cal)"
February 2012
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29
第一个例子因为把空格,换行符作为分隔符因此格式没有保留。
3.3 单引号(Single Quotes)
单引号可以使抑制所有的扩展(expansions),例如:
[me@linuxbox ~]$ echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
text /home/me/ls-output.txt a b foo 4 me
[me@linuxbox ~]$ echo "text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER"
text ~/*.txt {a,b} foo 4 me
[me@linuxbox ~]$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
转义字符(Escaping Characters)
有时我们只想引用一个字符。为此,我们可以使用反斜杠预先转义一个字符,在这个上下文中,它被称为转义字符。通常这是在双引号内进行的,以有选择地防止扩展(expansion)。
[me@linuxbox ~]$ echo "The balance for user $USER is: \$5.00"
The balance for user me is: $5.00
反斜杠转义序列(BACKSLASH ESCAPE SEQUENCES)
反斜杠也用来表示被称为“控制码(controlled code)” Table 7-2: Backslash Escape Sequences
| Escape Sequence | Meaning |
|---|---|
| \a | Bell (“alert”—causes the computer to beep) |
| \b | Backspace |
| \n | Newline (on Unix-like systems, this produces a linefeed) |
| \r | Carriage return |
| \t | Tab |
以上是常用的转义序列;
在echo后面加上-e可以解释为转义序列,也可以放在$' '字符中 下面例子中使用sleep命令来实现等待指定秒数后然后打印并响铃:
sleep 10; echo -e "Time's up\a"
也可以换一种写法:
sleep 10; echo "Time's up" $'\a'