Overview
wildcard是由shell处理的, 它只会出现在 command的argument 里——既不用在 command_name里, 也不用在 options 上。当在argument中碰到Wildcard时,shell会将其当作路径或文件名去在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行代换(路径扩展);否则就将该wildcard作为一个普通字符传递给command,交由command自行处理。总而言之,wildcard 实际上就是一种shell实现的路径扩展功能。在 wildcard 被处理后, shell会先完成该命令的重组,然后再继续处理重组后的命令,直至执行该命令。
例如,若当前目录下有Cha1、Cha2和Des三个文件,而我想用grep在Des中搜索包含字符串Cha的行,于是写出命令如下:
grep Cha* Des ①
当该命令交由shell处理时,首先会将Cha*中的*当作是一个wildcard,于是就会在当前目录中搜索可能的匹配。*作为wildcard而言匹配的是0个或多个的任意字符,于是文件Cha1和Cha2符合匹配要求,shell自行完成了该命令的重组,重组后的命令为:
grep Cha1 Cha2 Des ②
而这才是最终执行的命令的文本形式。所以命令①实际上的动作是试图在文件Cha2和Des中寻找包含Cha1字符串的行。这和期望grep所作的动作是大相径庭的。
可是,如果当前目录下没有可以匹配Cha*的文件或是文件夹(路径),那么shell会因为找不到可能的匹配而放弃*号的代换,将其传递给command处理,重组后的命令如下:
grep Cha* Des ③
这也是该情况下最终执行的命令的文本形式。在这种情况下命令①的动作和预期的动作却也不是一致的。因为当*号交给grep处理时,*号将不再是表示0个或多个任意的字符了——这是它作为wildcard,在shell中处理时的含义——在grep的处理中,*号是被当作正则表达式中的符号,表示的是其前面的字符出现0次或多次。
若是在第一种情况下,如何才能使重组后的命令为命令③,而非命令②呢?在shell的命令中,所有的文字可以分为meta 与 literal:literal就是普通的纯文字,对于shell来说没有什么特别的意义;meta则是shell中具有特定功能的特殊保留字符,如< > |等。不严格区分的话,wildcard也可以归入这一类。也就是说,meta就是会在shell中被处理的从而在最终用于执行的命令中丧失了其自身文本形式的特殊字符(从这个角度来说,将wildcard归入meta是有些不妥的,因为wildcard的有可能被替换掉也有可能不被替换)。若是希望能够将shell中的meta以其文本形式进入command的最终执行形态中——像前面我们所希望的一样,就必须告诉shell不要对meta进行处理,command需要使用它们的文本形式。这个工作则是由shell quoting(转义)来完成的。这种处理正是使用regular expression(正则表达式)所必需要用到的:因为regular expression(正则表达式)中有许多特殊字符(可以看作是RE中的meta)和shell中的meta及wildcard是相同的,所以为了让那些regular expression中的特殊字符能够通过shell传入regular expression就必须对其进行转义。同样,在那些定义了自有的meta的命令中,若是自有的meta与shell中的meta或wildcard重复,也要用到shell quoting,如tr。
Wildcard
* 匹配 0 或多个字符
? 匹配任意单一字符
[list] 匹配 list 中的任意单一字符
[!list] 匹配不在 list 中的任意单一字符
{string1,string2,...} 匹配 sring1 或 string2 (或更多)其一字符串
例:
a*b a与b之间可以有任意长度的任意字符, 也可以一个也没有, 如aabcb, axyzb, a012b, ab。
a?b a与b之间必须也只能有一个字符, 可以是任意字符, 如aab, abb, acb, a0b。
a[xyz]b a与b之间必须也只能有一个字符, 但只能是 x 或 y 或 z, 如: axb, ayb, azb。
a[!0-9]b a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
a{abc,xyz,123}b a与b之间只能是abc或xyz或123这三个字符串之一。
shell中的meta
下面是一些常用的:
IFS 由 <space> 或 <tab> 或 <enter> 三者之一组成(我们常用 space )。
CR 由 <enter> 产生。
= 设定变量。
$ 作变量或运算替换(请不要与 shell prompt 搞混了)。
> 重导向 stdout。
< 重导向 stdin。
| 命令管线。
& 重导向 file descriptor ,或将命令置于背境执行。
( ) 将其内的命令置于 nested subshell 执行,或用于运算或命令替换。
{ } 将其内的命令置于 non-named function 中执行,或用在变量替换的界定范围。
; 在前一个命令结束时,而忽略其返回值,继续执行下一个命令。
&& 在前一个命令结束时,若返回值为 true,继续执行下一个命令。
|| 在前一个命令结束时,若返回值为 false,继续执行下一个命令。
! 执行 history 列表中的命令。
Shell Quoting
一共有三种转义字符,它们实际上也可以看作是shell中的meta:
‘’(单引号):
又叫hard quote,其内部所有的shell meta都会被关掉。注意,hard quote中不允许出现’(单引号)。
“”(双引号):
又叫soft quote,其内部只允许出现特定的shell meta:
$ 用于参数代换
` 反引号,用于命令代换
/$ 实现美元标志
/’ 实现反引号的文本化(去除反引号的特殊意义)
/” 实现双引号的文本化(去除双引号的特殊意义)
// 实现反斜杠的文本化(去除反斜杠的特殊意义)
注意,在soft quote中单引号没有特殊意义,就是文本。
/(反斜杠):
又叫escape,去除其后紧跟的meta或wildcard的特殊意义。
实际上quote的使用就是为了跳过shell对特殊字符的处理。
Regular Expression
锚点(anchor):
用以标识 RE 于句子中的位置所在. 常见有:
^ 表示句首. 如 ^abc 表示以 abc 开首的句子.
$ 表示句尾. 如 abc$ 表示以 abc 结尾的句子.
/< 表示词首. 如 /<abc 表示以 abc 开首的词.
/> 表示词尾. 如 abc/> 表示以 abc 结尾的词.
修饰字符(modifier):
独立表示时本身不具意义, 专门用以修改前一个字元集的出现次数. 常见有:
* 表示前一个字元集的出现次数为0或多次。如ab*c表示a与c之间可有0或多个b存在。
? 表示前一个字元集的出现次数为0或1次。如ab?c表示a与c之间可有0或1个b存在。
+ 表示前一个字元集的出现次数为1或多次。如ab+c表示a与c之间可有1或多个b存在。
{n} 表示前一个字元集的出现次数必须为n次. 如ab{3,}c表示 a与c之间必须有3个b存在。
{n,} 表示前一个字元集的出现次数至少为n次. 如ab{3,}c表示a与c之间至少有3个b存在。
{n,m} 表示前一个字元集的出现次数为n到m次. 如ab{3,5}c表示a与c之间有3到5个b存在。
总结
总的来说,正是因为shell中的meta、wildcard有时会和command中的meta相同,为了让command中的meta不被shell解析以至于改变,就必须用shell quoting来保证其文字不变性。
附:shell脚本的解释过程
需要注意的是,double quote中的内容跳过了1-4步和9-10步,single quote中的内容跳过了1-10步。也就是说,double quote只经过参数扩展、命令代换和算术代换就可以送入执行步骤,而single quote直接会被送入执行步骤。而且,无论是double quote还是single quote在执行的时候能够告诉各个命令自身内部是一体的,但是其本身在执行时是并不是命令中文本的一部分。
如
Lsdetail=”ls –l”
$Lsdetail
执行的就是ls –l,而double quote并不是执行命令的一部分。