2007年6月3日 星期日

基本 bash 程式設計 (3) - String Operators

由於 shell script 重點相當著重在字串的處理,當然在 Linux 中有提供許多指令可以來進行字串的處理;然後,透過一些小技巧,就可以免去使用 pipe 搭配其他指令的麻煩,以下就慢慢來介紹...

String Substitution

在宣告變數時,有可能會因為疏忽而打錯;或是在寫給使用者使用的 script,每次都要詳細指定參數的麻煩....

其實這些錯誤或麻煩都可以避免,只要稍微修改一下變數的使用方式(搭配符號「{ }」)即可,以下用一張圖表來說明:

Operator
作用
目的
範例
${varname:-word}
假如 varname 變數存在,並且不是 null,則回傳變數內容;反之則回傳 word
用於設定變數預設值
${count:-0}
若變數 count 未定義,則回傳 0
${varname:=word}
假如 varname 變數存在,並且不是 null,則回傳變數內容;反之則設定此變數的值為 word,再回傳變數的值 用於將未定義的變數設定預設值
${count:=0}
若變數 count 未定義,則設定 count 變數的值為 0 並回傳
${varname:?message}
假如 varname 變數存在,並且不是 null,則回傳變數內容;反之則顯示「varname: message」,並中斷目前的命令或 script。 用來針對未定義的變數進行 debug
${count:?"undefined!"}
若變數 count 未定義,則會顯示「count: undefined!」,並中斷程式執行
${varname:+word}
假如 varname 變數存在,並且不是 null,則回傳 word 用來測試變數是否存在
${count:+1}
若變數 count 存在,則回傳 1
${varname:offset:length}
回傳 varname 變數的值中,從第 offset 的字元開始(變數值的第一個位置為 0),長度為 length 的字串。
有以下幾種用法:
  1. 若 length 省略,則從 offset 位置取到最後
  2. 若 offset 小於 0,表示從後面的位置往回取
  3. 若 varname 為 @,則回傳第 offset 個參數開始算的第 length 個參數

取得變數所儲存字串的部分字串
count="frogfootman"
${count:4} => footman
${count:4:4} => foot

像是表格中的第一種用法,就相當適用於必須為變數指定預設值的情形下。

以下直接用個例子來說明使用方式,假設我們檔案「sort-data.txt」,內容如下:
8 eight
9 nine
1 one
2 two
3 three
7 seven
5 five
6 six
4 four
0 zero
接著要寫一支 script 來根據最前面的數字作排序,並顯示指定的行數:
#!/bin/bash
# 檔案名稱:highest.sh

# 檢查 file 名稱是否有輸入
fileName=${1:?"filename missing, please check your file."}

# 設定預設顯示的行數
# 若使用者未輸入第二個參數就預設為 5
displayLine=${2:-5}

# sort 是用來排序的程式
# 參數「n」指定以數字為依據作排序
# 參數「r」則是 reverse 的意思,由大排到小
# head 是用來取「前幾行」
sort -nr $fileName | head -$displayLine
執行結果如下:
# 沒指定檔名時
shell> sh highest.sh
highest.sh: line 5: 1: filename missing, please check your file.

# 未指定所要顯示的行數
shell> sh highest.sh sort-data.txt
9 nine
8 eight
7 seven
6 six
5 five

# 指定顯示行數
shell> sh highest.sh sort-data.txt 3
9 nine
8 eight
7 seven


Patterns & Pattern Matching

說到字串處理,怎麼可以缺乏正規表示式呢?

當然 bash 中提供的不是正規表示式,不過提供的也是類似的功能,以下用一個表格來介紹:

Operator
作用
${variable#pattern}
從字串開頭開始比對,當 pattern 符合變數中的值,即不需繼續比對,僅比對至最短的符合字串,刪除比對到的部分並回傳剩下的變數值
${variable##pattern} 同上,會回傳刪除比對後的變數值,但所採取的是貪婪比對,會一直比對到最長的部分為止
${variable%pattern} 從字串尾端開始比對,當 pattern 符合變數中的值,即不需繼續比對,僅比對至最短的符合字串,刪除比對到的部分並回傳剩下的變數值
${variable%%pattern}
同上,會回傳刪除比對後的變數值,但所採取的是貪婪比對,會一直比對到最長的部分為止
${variable/pattern/string}
差異處:
在 variable 與 pattern 比對後,在上面的 operator 中,只有第一個比對到的字串會被更換為 string;在下面的 operator 中則會取代所有比對到的字串。
相同處:
  1. 若 pattern 開始加上「#」,就會比對開頭
  2. 若 pattern 開始加上「%」,則會比對結尾
  3. 若 string 沒有指定,則比對符合的部分會刪除(其實就是取代為空字串啦!)
  4. 若 variable 為「@」或「*」,則會逐一比對所有 position parameter
${variable//pattern/string}

以下直接用範例來說明:
myPath="/home/cam/book/long.file.name"
# 將兩個「/」之間的內容都刪除(貪婪比對)
echo ${myPath##/*/} # long.file.name
# 效果同上(因為 * 也可以代表 /)
echo ${myPath##*/} # long.file.name
# 將兩個「/」之間的內容刪除
echo ${myPath#/*/} # cam/book/long.file.name
# 從尾端開始比對,將比對符合結果刪除
echo ${myPath%.*} # /home/cam/book/long.file
# 從尾端開始比對,將比對符合結果刪除(貪婪比對)
echo ${myPath%%.*} # /home/cam/book/long

myPhotoName="myphoto.png"
# 將副檔名 png 改為 jpg
echo ${myPhotoName%.png}.jpg # myphoto.jpg

myEnvPath="/home/user/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin"
# 將「:」全部換成斷行字元,結果如下:
# /home/user/bin
# /usr/local/bin
# /bin
# /usr/bin
# /usr/X11R6/bin
echo -e ${myEnvPath//:/'\n'}
繼續提到上面說的 pattern,有更進階的用法,可以進行多重比對,只要把每個 pattern 以符號「|」相隔即可,以下用表格來說明:
Operator
說明
範例
效果
*(PatternList)
符合 0 個到多個指定的 pattern 清單
*(alice|hatter|hare)
字串中出現 alice 或是 hatter 或是 hare,甚至完全沒有,都符合
+(PatternList) 符合 1 個到多個指定的 pattern 清單 +(alice|hatter|hare) 字串中出現 alice 或是 hatter 或是 hare 都符合
+[0-9]
僅數字符合
?(PatternList) 符合 0 個到 1 個指定的 pattern 清單 ?(alice|hatter|hare) 字串中僅出現一次 alice 或是 hatter 或是 hare,或是完全沒有,就符合
@(PatternList) 剛好完全符合指定 pattern 清單的其中 1 個
@(alice|hatter|hare) 僅有 alice、hatter、hare 這三個字串符合(精確比對)
!(PatternList) 完全不符合指定的 pattern 清單
!(alice|hatter|hare) 只要字串中沒有 alice、hatter、hare 即符合
!(vt+([0-9]))
只要字串中沒有「vt+數字」的部分即符合

至於如何使用,就要靠使用者的創意與巧思了!


Length Operator

最後,要來介紹如何取得變數值的長度,這很簡單,語法如下:
${#varname}
以下來個簡單的範例:
myFile="myfile.txt"
echo ${#myFile} # 輸出 10

沒有留言:

張貼留言