Linux Shell的萬用字元與正則表示式
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並不是執行命令的一部分。