在本章,我们将介绍在按下Enter键时,命令行中发生的一些“神奇”事情。虽然我们会介绍shell的几个有趣而复杂的特性,但是我们只使用一条新命令来处理。
- echo:显示一行文本。
- *
- ~
- $
- ""
- ''
1.扩展
每次输入命令行按下Enter键时,bash都会在执行命令之前对文本进行多重处理。前面已经见过一个简单的字符序列(比如*)在shell中被识别为多种意思的几个例子。产生这个结果的处理过程称为扩展(expansion)。有了扩展功能,在输入内容后,这些内容将在shell对其执行之前被扩展成其他内容。为了证明这点,让我们先来看看echo命令。echo是shell的一个内置命令,它执行的任务非常简单,即把文本参数内容打印到标准输出。
- 路径名扩展
通过使用通配符来实现扩展的机制称为路径名扩展(pathname expansion)。试试在前面通过使用通配符来实现扩展的机制称为路径名扩展(pathname expansion)。试试在前面章节中使用过的一些技术,将会发现它们实际上就是扩展。下面给定一个主目录,如下所示:
执行下面的扩展:
隐藏文件的路径名扩展
众所周知,文件名以一个“.”点字符开头的文件都将被隐藏。路径名扩展功能也遵守这个规则。类似echo *这样的扩展并不能显示隐藏的文件。
乍一看,好像可以通过在扩展的模式中以一个点字符开头来包含隐藏文件,如下所示。
echo .*
。执行命令行ls –d .*|less可以发现这个结果是不正确的。
ls -d .[!.]?*
这种模式将扩展为以一个点字符开头的所有文件名,文件名中并不包含第二个点字符,但包含至少一个额外的字符,后面也可能还跟着其他的字符。
波浪线扩展
回顾前面对cd命令的介绍,你会发现波浪线字符(~)具有特殊的含义。如果把它用在一个单词的开头,那么它将被扩展为指定用户的主目录名;如果没有指定用户命名,则扩展为当前用户的主目录。
算术扩展
shell支持通过扩展来运行算术表达式。这允许我们把shell提示符当作计算器来使用。
算术扩展只支持整数(全是数字,没有小数),但是可以执行很多不同的运算。表1列出了一些支持的操作符。
空格在算术表达式中是没有意义的,而且表达式是可以嵌套的。例如把 52和3相乘。
下面的例子使用了除运算符和取余运算符,注意整数相除的结果。
花括号扩展
花括号扩展(brace expansion)可能算是最奇怪的扩展方式了。有了它,你可以按照花括号里面的模式创建多种文本字符串。实例如下。
用于花括号扩展的模式信息可以包含一个称为前导字符(preamble)的开头部分和一个称为附言(postscript)的结尾部分。花括号表达式本身可以包含一系列逗号分隔的字符串,也可以包含一系列整数或者单个字符。这里的模式信息不能包含内嵌的空白。下面的例子使用了一系列的整数。
花括号扩展支持嵌套。
那么花括号扩展一般应用在哪些地方呢?最普遍的应用是创建一系列的文件或者目录。比如说,摄影师有一个很大的图片集,想要按年份和月份来对这些图片进行分组,那么要做的第一件事就是创建一系列以年月格式命名的目录。这样,这些目录名将会按照年代顺序排列,输出目录的一个完整的列表。但是这样做工作量大,而且容易出错。为此我们可以这样操作。
参数扩展
本章我们只是简要地介绍参数扩展(parameter expansion),参数扩展用在 shell 脚本中比直接用在命令行中更为有用。它的许多特性与系统存储小块数据以及给每个小块数据命名的性能有关。很多这样的小块数据(称为变量[variable]会更合适)可用于扩展。例如,命名为USER的变量包含你的用户名,为了触发参数扩展,并显示出USER的内容,你可以进行如下操作。
命令替换--$()
命令替换可以把一个命令的输出作为一个扩展模式使用,如下所示。
这个功能并不只是局限于简单的命令,也可以应用于整个管道中(只不过只显示部分输出内容)。
2 引用--$
我们已经知道,shell有多种方式可以执行扩展,现在我们来学习如何控制扩展。先看下面的例子。符串。在第一个例子中,shell会对echo命令的参数列表进行单词分割(word splitting),去除多余的空白。在第二个例子中,因为$1是一个未定义的变量,所以参数扩展将把$1的值替换为空字shell提供了一种称为引用(quoting)的机制,用来有选择性地避免不想要的扩展。
双引号
我们要看的第一种引用类型是双引号(double quote)。如果把文本放在双引号中,那么shell使用的所有特殊字符都将失去它们的特殊含义,而被看成普通字符。字符“$”(美元符号)、“”(反斜杠)、“’”(反引号)除外。这就意味着单词分割、路径名扩展、波浪线扩展和花括号扩展都将失效,但是参数扩展、算术扩展和命令替换仍然生效。使用双引号能够处理文件名中包含空白的情况。假设不幸地有一个名为two words.txt的文件,如果在命令行中使用该文件名,那么单词分割功能将把它当成两个独立的参数,而不是当成我们希望的单个参数,具体运行结果如下所示。
请记住,参数扩展、算术扩展和命令替换在双引号中依然生效:
接下来,让我们看看双引号对字符替换的影响。我们首先深入了解一下单词分割是怎么工作的。在前面的例子中,我们已经看到单词分割去除文本中多余空白的情况,如下所示。默认情况下,单词分割会先查找是否存在空格、制表符以及换行(换行字符),然后把它们当作单词见的界定符(delimiter)。这就意味着没有用引号包含起来的空格、制表符和换行字符都不会被当成文本的一部分,而只是被当成分割符。因为它们把这些单词分割成不同的参数,所不会被当成文本的一部分,而只是被当成分割符。因为它们把这些单词分割成不同的参数,所以例子中的命令行被识别为命令后面跟着4个不同的参数。但是如果加上双引号,单词分割功能将失效,嵌入的空格将不再被当成界定符,而是被当成参数的一部分,如下所示。
单词分割机制会把换行字符当成界定符,这一点在命令替换时将会产生微妙有趣的效果。参考下面的例子。
单引号
如果我们希望抑制所有的扩展,那么应使用单引号。下面是不使用引号、使用双引号和使用单引号的情况对比。
转义字符
有时候我们只是想要引用单个字符。这种情况可以通过在该字符前加上反斜杠来实现。这里的反斜杠称为转义字符。转义字符经常在双引号中用来有选择性地阻止扩展。如下所示。
转义字符也常用来消除文件名中某个字符的特殊含义。比如,文件名中可以使用在shell中通常具有特殊含义的字符。这些字符包括“$”、“!”、“&”、空格等。要想在文件名中包含特殊字符,可执行如下操作。
反斜杠除了作为转义字符外,也是一种表示法的一部分,这种表示法代表称为控制码的某些特殊字符。ASCII码表的前32个字符用来向电传打字类设备传送命令。其中有一些控制码很常见(比如制表符、退格符、换行符和回车符),但是其他的都不太常见(空字符、结束符和确认符),如表 2所示。
在echo命令中带上-e选项,就能够解释转义字符序列。也可以将其放在“$ ‘ ’”
在echo命令中带上-e选项,就能够解释转义字符序列。也可以将其放在“$ ‘ ’”中。在下面的例子中,只需要使用sleep命令(它是一个简单的程序,在等待指定的秒数之后就会退出),就可以创建一个简单的倒计时的计时器:
随着我们输入学习shell,就会发现扩展和引用的使用频率逐渐多起来,所以我们有必要很好地理解它们的工作方式。事实上,甚至可以说它们是shell中最重要的主题。如果不能正确地理解扩展,那么shell将会一直是个神秘和让人困惑的资源,它的潜在能力也就被浪费了。