shell中的通配符与正则表达式

转载 2006年05月21日 15:31:00

wildcard & regular expression

话说写在shell script里的东东,一旦是头头上添上了sha bang(#! /bin/bash),通通要经过shell层层剥开皮检查一番,这样以来,参数不再是原来的参数,而是被shell解析程序施加了7层过滤的参数(7 types of expansion),这让我们在传参数时,心里很不踏实。

这里一种办法,用于区分shell展开时对文件名的处理即pathname expansion与命令中参数中常用的正则表达式的处理RE matching
Part-I: Wildcard
首先, wildcard 也是屬於 command line 的處理工序, 作用於 argument 裡的 path 之上.
沒錯, 它不用在 command_name 也不用在 options 上.
而且, 若 argument 不是 path 的話, 那也與 wildcard 無關.
換句更為精確的定義來講, wildcard 是一種命令行的路逕擴展(path expansion)功能.
提到這個擴展, 那就不要忘記了 command line 的"重組"特性了!
是的, 這與變量替換(variable substitution)及命令替換(command substitution)的重組特性是一樣的!
也就是在 wildcard 進行擴展後, 命令行會先完成重組才會交給 shell 來處理.

了解了 wildcard 的擴展與重組特性後, 接下來, 讓我們了解一些常見的 wildcard 吧:
*: 匹配 0 或多個字元
?: 匹配任意單一字元
[list]: 匹配 list 中的任意單一字元(註一)
[!list]: 匹配不在 list 中的任意單一字元
{string1,string2,...}: 匹配 sring1 或 string2 (或更多)其一字串
(註一: list 可以為指定的個別字元, 如 abcd; 也可以為一段 ASCII 字元的起止範圍, 如: a-d .)

例:
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 這三個字串之一, 如 aabcb, axyzb, a123b 這三個.

注意:
1) [! ] 中的 ! 只有放在第一順位時, 才有排除之功. 舉例說:
[!a]* 表示當前目錄下所有不以 a 開首的路逕名稱.
/tmp/[a/!]* 表示 /tmp 目錄下以 a 或 ! 開首的路逕名稱. (思考: 為何 ! 前面要加 / 呢? 提示: 十三問之 4 )

2) [ -] 中的 - 左右兩邊均有字元時, 才表示一段範圍, 否則僅作 "-"(減號) 字元來處理. 舉例說:
/tmp/*[-z]/[a-zA-Z]* 表示 /tmp 目錄下所有以 z 或 - 結尾的子目錄下以英文字母(不分大小寫)開首的路逕名稱.

3) 以 * 或 ? 開首的 wildcard 不能匹配隱藏文件(即以 . 開首的文件). 舉例說:
*.txt 並不能匹配 .txt 但可匹配 1.txt 這樣的路逕名稱.
但 1*txt 及 1?txt 均可匹配 1.txt 這樣的路逕名稱.

基本上, 要掌握 wildcard 並不難, 只要多加練習, 再勤於思考, 就能熟加運用了.
再次提醒: 別忘了"擴充+重組"這個重要特性, 而且只作用在 argument 的 path 上.
比方說, 假設當前目錄下有 a.txt b.txt c.txt 1.txt 2.txt 3.txt 這幾份文件.
當我們在命令行中下達 ls -l [0-9].txt 的命令行時,
因為 wildcard 處於 argument 的位置上, 於是根據其匹配的路逕, 擴展為 1.txt 2.txt 3.txt ,
再重組出 ls -l 1.txt 2.txt 3.txt 這樣的命令行.
因此, 你在命令行上敲 ls -l [0-9].txt 與 ls -l 1.txt 2.txt 3.txt 都是同樣的結果, 其原因正是於此了... :)


Part-II: Regular Expression
接下來的 Regular Expression(RE) 可是個大題目, 要講的很多, 我這裡當然不可能講得很完全.
只希望帶給大家一個基本的入門概念, 就很是足夠了...

先來考一下英文好了: What is expression?
簡單來說, 就是"表達", 也就是人們在溝通時所要陳述的內容.
然而, 生活中, 表達方要清楚的將意思描述清楚而讓接收方完整且無誤的領會, 可不是件容易的事情.
因而才會出現那麼多的"誤會", 真可嘆句"表達不易"啊....
同樣的情形也發生在電腦的資料處理過程中, 尤其是當我們在描術一段"文字內容"的時候...
那麼, 我們不禁要問: 有何方法可以讓大家的誤會降至最低程度而讓表達的精確度達到最高程度呢?
答案就是"標準化"了, 亦就是我們這裡要談的 Regular Expression 啦....  ^_^

然而, 在進入 RE 介紹之前, 不防先讓我們溫習一下 shell 十三問第 4 問, 也就是關於 quoting 的部份.
關鍵是要能夠區分 shell command line 上的 meta 與 literal 這兩種不同的字符類別.
然後, 我這裡才跟你講:
--- RE 表達式裡的字符也是分為 meta 與 literal 這兩種!
呵, 不知親愛的讀者是否被我搞混亂了呢? ...  ^_^
這也難怪啦, 因為這的確是最容易混亂的地方, 剛學 RE 的朋友很多時候都死在這裡!
因此請特別小心理解哦...
簡單而言, 除非你將 RE 寫在特定程式使用的腳本裡,
否則, 我們的 RE 也是透過 command line 輸入的.
然而, 不少 RE 所始用的 meta 字符, 跟 shell meta 字符是衝突的.
比方說, * 這個字符, 在 RE 裡是一個 modifier(後述), 在 command line 上, 卻是個 wildcard !

那麼, 我們該如何解決這樣的衝突呢? 關鍵就是看你對十三問第 4 問所提的 quoting 是否夠理解了!
若你明白到 shell quoting 就是在 command line 上關閉 shell meta 這一基本原理,
那你就能很輕鬆的解決 RE meta 與 shell meta 的衝突問題了:
--- 用 shell quoting 關掉 shell meta 就是了!
就這麼簡單...  ^_^
再以剛提到的 * 字符為例, 若在 command line 中沒有 quoting 處理的話, 如 abc* ,
那就會被作為 wildcard expansion 來擴充及重組了.
若將之置於 quoting 中, 如 "abc*", 則可避免 wildcard expansion 的處理.

好了, 說了大半天, 還沒進入正式的 RE 介紹呢...
大家別急, 因為我的教學風格就是要先建立基礎, 循序漸進的...  ^_^
因此, 我這裡還要在囉唆一個觀念, 才會到 RE 的說明啦... (哈... 別打我....)
當我們在談到 RE 時, 千萬別跟 wildcard 搞混在一起!
尤其在 command line 的位置裡, wildcard 只作用於 argument 的 path 上.
但是 RE 卻只用於"字串處理"的程式之中, 這與路逕名稱一點關系也沒有!
RE 所處理的字串通常是指純文檔或透過 stdin 讀進的內容...

okay, 夠了夠了, 我已看到一堆人開始出現不大耐煩的樣子了....  ^_^
現在, 就讓我門登堂入室, 撩開 RE 的神秘面紗吧, 這樣可以放過我了吧? 哈哈...

在 RE 的表達式裡, 主要分兩種字符(character): literal 與 meta.
所謂 literal 就是在 RE 裡不具特殊功能的字符, 如 abc, 123 這些;
而 meta 在 RE 裡具有特殊的功能, 要關閉之需在 meta 前面使用 escape( / )字符.

然而, 在介紹 meta 之前, 先讓我們來認識一下字符組合(character set)會更好些.
所謂的 char. set 就是將多個連續的字符作一個集合, 比方說:
abc: 表示 abc 三個連續的字符, 但彼此獨立而非集合. (可簡單視為三個 char. set)
(abc): 表示 abc 這三個連續字符的集合. (可簡單視為一個 char. set)
abc|xyz: 表示或 abc 或 xyz 這兩個 char. set 之一.
[abc]: 表示單一字符, 可為 a 或 b 或 c . (與 wildcard 之 [abc] 原理相同)
[^abc]: 表示單一字符, 不為 a 或 b 或 c 即可. (與 wildcard 之 [!abc] 原理相同)
. : 表示任意單一字符. (與 wildcard 之 ? 原理相同)

在認識了 char. set 這個概念後, 然後再讓我們多認識幾個 RE 中常見的 meta 字符:

- 錨點(anchor)
用以標識 RE 於句子中的位置所在. 常見有:
^: 表示句首. 如 ^abc 表示以 abc 開首的句子.
$: 表示句尾. 如 abc$ 表示以 abc 結尾的句子.
/<: 表示詞首. 如 /<abc 表示以 abc 開首的詞.
/>: 表示詞尾. 如 abc/> 表示以 abc 結尾的詞.

- 修飾字符(modifier)
獨立表示時本身不具意義, 專門用以修改前一個 char. set 的出現次數. 常見有:
*: 表示前一個 char. set 的出現次數為 0 或多次. 如 ab*c 表示 a 與 c 之間可有 0 或多個 b 存在.
?: 表示前一個 char. set 的出現次數為 0 或 1 次. 如 ab?c 表示 a 與 c 之間可有 0 或 1 個 b 存在.
+: 表示前一個 char. set 的出現次數為 1 或多次. 如 ab+c 表示 a 與 c 之間可有 1 或多個 b 存在.
{n}: 表示前一個 char. set 的出現次數必須為 n 次. 如 ab{3,}c 表示 a 與 c 之間必須有 3 個 b 存在.{n,}: 表示前一個 char. set 的出現次數至少為 n 次. 如 ab{3,}c 表示 a 與 c 之間至少有 3 個 b 存在.
{n,m}: 表示前一個 char. set 的出現次數為 n 到 m 次. 如 ab{3,5}c 表示 a 與 c 之間有 3 到 5 個 b 存在.

然而, 當我們在識別 modifier 時, 卻很容易忽略"邊界(boundary)"字符的重要性.
以剛提到的 ab{3,5}c 為例, 這裡的 a 與 c 就是邊界字符了.
若沒有邊界字符的幫忙, 我們很容以作出錯誤的解讀.
比方說: 我們用 ab{3,5} 這個 RE (少了 c 這個邊界字符)可以抓到 abbbbbbbbbbc (a 後有 10 個 b )這串字嗎?
從剛才的 modifier 我們一般會認為我們要的 b 是 3 到 5 個, 若超出了此範圍, 就不是我們要表達的.
因此, 我們或會很輕率的認為這個 RE 抓不到結果...
然而答案卻是可以的! 為甚麼呢?
讓我們重新解讀 ab{3,5} 這個 RE 看看:
我們要表達的是 a 後接 3 到 5 個 b 即可, 但 3 到 5 個 b 後面我們卻沒規定是甚麼,
因此在 RE 後面可以是任意的文字, 當然包括 b 也可以啦! (明白了嗎?)
同樣的, 我們用 b{3,5}c 也同樣可以抓到 abbbbbbbbbbc 這串字的.
但我們若使用 ab{3,5}c 這樣的 RE 時, 由於同時有 a 與 c 這兩個邊界字符, 那就截然不同了!

有空再思考一下, 為何我們用下面這些 RE 都可抓到 abc 這串字呢?
x*
ax*, abx*, ax*b
abcx*, abx*c, ax*bc
bx*c, bcx*, x*bc
...(還有更多...)
但, 若我們在這些 RE 前後分別加一個 ^ 與 $ 這樣的 anchor, 那又如何呢?

剛學 RE 時, 只要能掌握上面這些基本的 meta 大蓋就可以入門了.
一如前述, RE 是一種規範化的文字表達方式, 主要用於某些文字處理工具之間,
如 grep, perl, vi, awk, sed, 等等. 常用以表示一段連續的字串, 捕獲之或替換之.
然而, 每種工具對 RE 表達式的具體解讀或有一些細微差異, 不過, 基本原則還是一致的.
只要能掌握 RE 的基本原理, 那就一理通百理明了, 只是在實作時稍加變通即可.

比方以 grep 來說, 在 linux 上你可找到 grep, egrep, fgrep 這幾個程式, 其差異大致如下:

* grep:
傳統的 grep 程式, 在沒有參數的情況下, 只輸出符合 RE 字串之句子. 常見參數如下:
-v: 逆反模示, 只輸出"不含" RE 字串之句子.
-r: 遞迴模式, 可同時處理所有層級子目錄裡的文件.
-q: 靜默模式, 不輸出任何結果(stderr 除外. 常用以獲取 return value, 符合為 true, 否則為 false .)
-i: 忽略大小寫.
-w: 整詞比對, 類似 /<word/> .
-n: 同時輸出行號.
-c: 只輸出符合比對的行數.
-l: 只輸出符合比對的文件名稱.
-o: 只輸出符合 RE 的字串. (gnu 新版獨有, 不見得所有版本都支持.)
-E: 切換為 egrep .

* egrep:
為 grep 的擴充版本, 改良了許多傳統 grep 不能或不便的操作. 比方說:
- grep 之下不支持 ? 與 + 這兩種 modifier, 但 egrep 則可.
- grep 不支持 a|b 或 (abc|xyz) 這類"或一"比對, 但 egrep 則可.
- grep 在處理 {n,m} 時, 需用 /{ 與 /} 處理, 但 egrep 則不需.
諸如此類的... 我個人會建議能用 egrep 就不用 grep 啦...  ^_^

* fgrep:
不作 RE 處理, 表達式僅作一般字串處理, 所有 meta 均失去功能.


(順道一提: eval )

講到 command line 的重組特性, 真的需要我們好好的加以理解的.
如此便能抽絲剝襺的一層層的將正個 command line 分析得一清二楚, 而不至於含糊.
假如這個重組特性理解下來來, 那麼, 接下來我們介紹一個好玩的命令 --- eval .

我們在不少變量替換的過程中, 常碰到所謂的複式變量的問題, 如:
[code]a=1
A1=abc[/code]
我們都知道 echo $A1 就可得到 abc 這個結果.
然而, 我們能否用 $A$a 來取代 $A1 而同樣替換出 abc 呢?

這個問題我們可用很輕鬆的用 eval  來解決:
[code]eval echo /$A$a[/code]
說穿了, eval 只不過是在命令行完成替換重組後, 再來一次替換重組罷了...
就是這麼簡單啦~~~   ^_^
---------------------------------------------
另外,关于sed 和 awk 中使用的 正则表达式支持的一些meta 字符和anchor 标签,跟grep(所谓的g/RE/p)类似。
:shell对命令行输入的处理

相关文章推荐

《shell中的通配符,特殊字符和正则表达式》

1. shell支持的通配符 2. shell中的特殊字符(以bash为例) 3. 正则表达式

shell 中的通配符和正规表示法(正则表达式)

shell 中的通配符和正规表示法(正则表达式)

grep和find的参数疑云: 左手shell通配符,右手正则表达式

grep 和 find。 对于使用linux的筒子们来说,每天都要祭出它们很多遍。 因为太熟,反而会将错误用法在它们身上一遍遍上演。 比如grep, 我最最常用下面一条: grep -rin "c...

shell字符处理命令,通配符,正则表达式

shell中正则表达式和通配符【linux上实际操作是什么情况,就是什么情况,理论只是猜测,试验结果大于一切】 通配符一般是用于匹配符合条件的文件名(例如:ls fei*hai),而正则表达式则一...

正则表达式或通配符匹配的代码

  • 2014年06月15日 11:24
  • 26KB
  • 下载

linux初学习之正则表达式和通配符

一、正则表达式: 元字符是用来阐释字符表达式意义的字符,简言之,就是用来描述字符的字符。 正则表达式RE(Regular Expression)是由一串字符和元字符构成的字符串。 正则表达式的主...

正则表达式,通配符

比如QString str = “123你好456你好”我们要从这个str中提取出“123456” QString str = “123你好456你好”; QString tmp; fo...
  • psujtfc
  • psujtfc
  • 2015年03月04日 10:47
  • 405

linux 通配符与正则表达式(grep、find) (转载)

原文地址: http://hi.baidu.com/homappy/blog/item/4837a8fbed49312f4e4aea5d.html 首先要记住的是: 正则表达式与通配符不一样,它们表示...
  • yyj8255
  • yyj8255
  • 2011年06月14日 10:37
  • 490

Linux通配符与正则表达式

一、 基本概念 1. 什么是通配符:具有特殊含义的符号。 2. 什么是正则表达式:一种用来进行字符串查找、匹配、分割、替换的语法规则。 二、基本符号 1. 通配符的基本符号有: *, ?, [...

通配符&正则表达式

通配符 “通配符是系统级别的 ⽽而正则表达式需要相关⼯工具和语⾔言的⽀支持: egrep, awk, vi,perl。 当您键入 ls .txt 命令并按 Enter 后,寻找哪些文...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:shell中的通配符与正则表达式
举报原因:
原因补充:

(最多只允许输入30个字)