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通配符与正则表达式

shell通配符 1.命令在shell中的一般执行过程: 2.shell的通配符 3.转义与引用 正则表达式 1.基本正则表达式(Basic Regular Express...
  • whxlovehy
  • whxlovehy
  • 2010年12月03日 11:27
  • 1551

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

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

通配符与正则表达式的关系

关系 首先,先给出关系。 通配符 正则表达式 * (.)* ? (.)? . \. 本人在开发中,时常会把正则和通配符搞混,甚至一度以为通配是正则的子集。通配是一种简单的...
  • tanchen1991
  • tanchen1991
  • 2014年12月20日 13:08
  • 1383

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

1. shell支持的通配符 2. shell中的特殊字符(以bash为例) 3. 正则表达式
  • miss_acha
  • miss_acha
  • 2015年03月03日 14:04
  • 5930

正则表达式 和 通配符

正则表达式 正则表达式主要是用来描述一个句法规则的模式。其实说的通俗一点,就是利用字符和元字符的组合,对一些符合既定句法的模式进行模糊匹配。它的主要功能是文本查询和字符串操作。 正则表达式...
  • Hsuxu
  • Hsuxu
  • 2012年08月02日 15:30
  • 2502

shell 脚本 正则表达式通配符之 "[]" [大三TJB_708]

在“[]“中可以有以下几种形式,每种形式的含义如后所述。   1.[list] 匹配list中的任意单一字符,只要文件中此行包含list中的某一字符都会被匹配。 例regular_expres...
  • misskissC
  • misskissC
  • 2012年11月08日 11:54
  • 3044

Shell正则表达式与通配符

通配符与正则表达式的区别: *等符号出现在命令提示符中就是通配符 *等符号出现在脚本中就是正则表达式 c         匹配字母C *      前面的字符可以出现0次或者多次 .       ...
  • VmBoys
  • VmBoys
  • 2016年05月19日 15:38
  • 281

将通配符*和?处理成正则表达式

 在开发中常碰到允许用户输入一个通配符,然后按照通配符来查找文件。我在.Net的类里面没有找到匹配通配符的类,所以写了一个函数来将输入的通配符转换成正则表达式。  static string Chan...
  • SpringLiu
  • SpringLiu
  • 2007年05月15日 13:25
  • 742

关于shell中的正则表达式和awk的使用

今天有幸得方叔开的小灶,特将课堂笔记整理如下,以备后续使用。         1、关于正则表达式 egrep和grep -e都可接正则表达式,若使用grep接正则表达式,则表达式中的"...
  • yingkongshi99
  • yingkongshi99
  • 2015年08月28日 21:58
  • 1479

UltraEdit使用正则表达式(通配符)进行查找和替换

UltraEdit是目前使得比较多的记事本替代方案。其对记事本的优势是不言而喻的。下面演示一下在UltraEdit中如何使用正则表达式(通配符)进行常用的查找和替换。 准备工作:在使用正则表达式前,...
  • great3779
  • great3779
  • 2012年01月30日 16:14
  • 35170
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:shell中的通配符与正则表达式
举报原因:
原因补充:

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