shell通配符 与 文本正则表达式:
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来保证其文字不变性。
实例:
[root@redhat script]#ll
总用量 40
-rw-r--r-- 1 root root 1 2011/12/06 05:12:25 1
-rwxrwxrwx 1 root root 103 2011/09/22 02:10:07 aa.bak
-rwxr-xr-x 1 root root 121 2011/12/06 06:51:07 aa.sh
drwxr-xr-x 2 root root 4096 2011/11/26 23:27:11 CD
-rwxr-xr-x 1 root root 44 2011/09/22 12:11:21 first.sh
-rwxrwxrwx 1 root root 195 2011/12/06 06:07:48 grade.txt
-rwxrwxrwx 1 root root 303 2011/11/27 03:04:04 list
-rwxrwxrwx 1 root root 101 2011/11/27 03:52:13 nameState
-rwxrwxrwx 1 root root 44 2011/11/27 03:24:34 sedscr
-rwxrwxrwx 1 root root 90 2011/09/21 20:39:22 test.sh
[root@redhat script]#grep aa* aa.sh
[root@redhat script]#
[root@redhat script]#
[root@redhat script]#grep ll* aa.sh
shopt -s expand_aliases
alias jesse='echo "I am Jesse"'
alias lll='ls -l'
lll /root/y*
[root@redhat script]#grep 'aa*' aa.sh
#/bin/bash
shopt -s expand_aliases
alias jesse='echo "I am Jesse"'
alias lll='ls -l'
aasdfdsf
sdfaads
[root@redhat script]#grep 'll*' aa.sh
shopt -s expand_aliases
alias jesse='echo "I am Jesse"'
alias lll='ls -l'
lll /root/y*
[root@redhat script]#
附: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并不是执行命令的一部分。
理解
1、在不严格区分的情况下shell的解释中,包括:meta(元字符) 与 literal(普通字符),meta包括一些wildcard(通配符)和一些其他的特殊字符。
2、在shell中能处理正则表达式的命令(command)中的解释中也包括有meta(元字符)。
3、若是希望能够将shell中的meta以其文本形式进入command的最终执行形态中,就是希望meta不要被shell解释而被能处理正则表达式的command来解释,就需要用到shell的quoting(转义)来完成的,quoting属于shell中的特殊字符。
下面分别给出shell中的元字符(也就是通配符和一些特殊字符)和command中的元字符
shell中的元字符
一些常用的通配符:
符号 | 意义 |
* | 代表『 0 个到无穷多个』任意字符 |
? | 代表『一定有一个』任意字符 |
[ ] | 同样代表『一定有一个在括号内』的字符(非任意字符)。例如 [abcd] 代表『一定有一个字符, 可能是 a, b, c, d 这四个任何一个』 |
[ - ] | 若有减号在中括号内时,代表『在编码顺序内的所有字符』。例如 [0-9] 代表 0 到 9 之间的所有数字,因为数字的语系编码是连续的! |
[^ ] | 若中括号内的第一个字符为指数符号 (^) ,那表示『反向选择』,例如 [^abc] 代表 一定有一个字符,只要是非 a, b, c 的其他字符就接受的意思。 |
{string1,string2,...} | 匹配 sring1 或 string2 (或更多)其一字符串 | a{abc,xyz,123}b a与b之间只能是abc或xyz或123这三个字符串之一。 |
一些常用的特殊字符:
符号 | 内容 |
# | 批注符号:这个最常被使用在 script 当中,视为说明!在后的数据均不执行 |
\ | 跳脱符号:将『特殊字符或通配符』还原成一般字符 |
| | 管线 (pipe):分隔两个管线命令的界定(后两节介绍); |
; | 连续指令下达分隔符:连续性命令的界定 (注意!与管线命令并不相同) |
~ | 用户的家目录 |
$ | 取用变数前导符:亦即是变量之前需要加的变量取代值 |
& | 工作控制 (job control):将指令变成背景下工作 |
! | 逻辑运算意义上的『非』 not 的意思! |
/ | 目录符号:路径分隔的符号 |
>, >> | 数据流重导向:输出导向,分别是『取代』与『累加』 |
<, << | 数据流重导向:输入导向 (这两个留待下节介绍) |
' ' | 单引号,不具有变量置换的功能 |
" " | 具有变量置换的功能! |
` ` | 两个『 ` 』中间为可以先执行的指令,亦可使用 $( ) |
( ) | 在中间为子 shell 的起始与结束 |
{ } | 在中间为命令区块的组合! |
IFS -----------由 <space> 或 <tab> 或 <enter> 三者之一组成(我们常用 space )。
CR-----------由 <enter> 产生。
= ------------设定变量。
&& -----------在前一个命令结束时,若返回值为 true,继续执行下一个命令。 *
|| --------------在前一个命令结束时,若返回值为 false,继续执行下一个命令。 *
command中的元字符
一些正则表达式中的特殊符号:
代表意义 | |
[:alnum:] | 代表英文大小写字符及数字,亦即 0-9, A-Z, a-z |
[:alpha:] | 代表任何英文大小写字符,亦即 A-Z, a-z |
[:blank:] | 代表空格键与 [Tab] 按键两者 |
[:cntrl:] | 代表键盘上面的控制按键,亦即包括 CR, LF, Tab, Del.. 等等 |
[:digit:] | 代表数字而已,亦即 0-9 |
[:graph:] | 除了空格符 (空格键与 [Tab] 按键) 外的其他所有按键 |
[:lower:] | 代表小写字符,亦即 a-z |
[:print:] | 代表任何可以被打印出来的字符 |
[:punct:] | 代表标点符号 (punctuation symbol),亦即:" ' ? ! ; : # $... |
[:upper:] | 代表大写字符,亦即 A-Z |
[:space:] | 任何会产生空白的字符,包括空格键, [Tab], CR 等等 |
[:xdigit:] | 代表 16 进位的数字类型,因此包括: 0-9, A-F, a-f 的数字与字符 |
一些常用的基础正则表达式字符:
RE 字符 | 意义与范例 |
^word | 意义:待搜寻的字符串(word)在行首! grep -n '^#' regular_express.txt |
word$ | 意义:待搜寻的字符串(word)在行尾! grep -n '!$' regular_express.txt |
. | 意义:代表『一定有一个任意字符』的字符! grep -n 'e.e' regular_express.txt |
\ | 意义:跳脱字符,将特殊符号的特殊意义去除! grep -n \' regular_express.txt |
* | 意义:重复零个到无穷多个的前一个 RE 字符 grep -n 'ess*' regular_express.txt |
[list] | 意义:字符集合的 RE 字符,里面列出想要撷取的字符! grep -n 'g[ld]' regular_express.txt |
[n1-n2] | 意义:字符集合的 RE 字符,里面列出想要撷取的字符范围! grep -n '[0-9]' regular_express.txt |
[^list] | 意义:字符集合的 RE 字符,里面列出不要的字符串或范围! grep -n 'oo[^t]' regular_express.txt |
\{n,m\} | 意义:连续 n 到 m 个的『前一个 RE 字符』 grep -n 'go\{2,3\}g' regular_express.txt |
一些常用的扩展的正则表达式字符:
RE 字符 | 意义与范例 |
+ | 意义:重复『一个或一个以上』的前一个 RE 字符 egrep -n 'go+d' regular_express.txt |
? | 意义:『零个或一个』的前一个 RE 字符 egrep -n 'go?d' regular_express.txt |
| | 意义:用或( or )的方式找出数个字符串 egrep -n 'gd|good' regular_express.txt |
() | 意义:找出『群组』字符串 egrep -n 'g(la|oo)d' regular_express.txt |
()+ | 意义:多个重复群组的判别 echo 'AxyzxyzxyzxyzC' | egrep 'A(xyz)+C' 上面的例子意思是说,我要找开头是 A 结尾是 C ,中间有一个以上的 "xyz" 字符串的意思~ |