Bash One-Liners Explained (二):操作字串

發表於2013-10-21

這是 Bash One-Liners Explained 系列的第二篇文章。在這一篇裡,我會給你們介紹如何用 Bash 來完成各種各樣的字串操作。我會選擇用最好的 Bash 做法,各種常見的語法和技巧,向各位闡明如何用 Bash 內建的命令和 Bash 程式語言來完成各式各樣的任務。

1. 生成從 a 到 z 的字母表

這一行命令用到了括號展開(Brace expansion)功能,它可以用於生成任意的字串。{x..y}是一個序列表示式,其中 x 和 y 都是單個字元,這個表示式展開後包含 x 與 y 之間的所有字元。

執行上面的命令會生成從 a 到 z 的所有字母:

2. 生成從 a 到 z 的字母表,字母之間不包含空格

這是一個 99.99% 的人都不知道的非常棒的技巧。如果你在printf命令之後指定一個列表,最終它會迴圈依次列印每個元素,直到完成為止。

在這一行命令中,printf 的格式為"%c",代表一個字元(character),後面的引數是從 a 到 z 的字元列表,字元之間以空格分隔。所以,當printf執行時,它依次輸出每個字元直到所有字元全被處理完成為止。

下面是執行的結果:

輸出的結果最後不包含換行符,因為printf的輸出格式是"%c",其中並沒有包含\n。如果你想輸出完整的一行,可以簡單地在字元列表後面增加一個$’\n’:

另外一種方式是,通過 echo 來輸出 printf 的結果:

這一行命令用到了命令替換功能:執行printf "%c" {a..z}命令然後用執行的輸出替換命令。然後,echo 列印輸出結果時會帶上換行符。

如果你想要每一行僅輸出一個字母,在字元後面增加一個換行符:

輸出:

如果想要快速地將 printf 的結果儲存到變數中,可以使用-v選項:

結果會將abcdefghijklmnopqrstuvwxyz儲存到變數alphabet中。

類似地,你也可以利用同樣的語法生成一個數字列表,例如從1到100:

輸出:

或者,如果你忘記這種方法,可以使用 seq 命令來做這個事情:

3. 輸出從 00 到 09 的數字

這裡我們又用到了printf的迴圈輸出功能,這一次的輸出格式為"%02d ",意思是在輸出數字的時候,如果不滿兩位就用0補齊。同時,輸出的元素是 0 到 9的列表(括號展開後的結果)。

輸出結果:

如果你使用的是最新的 Bash 4 版本,你可以使用加強的括號展開功能:

老版本不包含該特性。

4. 生成 30 個英文單詞

這是一個濫用括號展開的例子,看看最終輸出的結果是什麼:

是不是很棒?

你可以通過括號展開生成一組單詞或者符號的排列。例如:

上面的命令會生成以下結果:a1 a2 a3 b1 b2 b3 c1 c2 c3。首先,它取出第一個括號中的第一個元素a,然後依次與第二個括號{1,2,3}的所有元素組合,生成a1 a2 a3,依此類推。

5. 重複輸出 10 次字串

這一行命令兩次利用了括號展開功能,字串foo與10個空字串組合,最終生成10分拷貝:

6. 拼接字串

這一行命令簡單地將兩個變數的值連線在一起,所以如果x變數的值為foo,而y的值為bar,則結果為foobar

注意,這裡的"$x$y"是加了雙引號的,如果忘記加了,echo會將$x$y當成常規的命令列引數去解析。所以,如果$x在開頭包含-,它就變成一個命令列引數,而不是被 echo 輸出的內容。

執行後的輸出:

相反,正確書寫的方式執行後的結果如下所示:

不過,如果你要將兩個字串相連的結果賦值給變數,是可以將雙引號省略的:

7. 分割字串

假設變數$str的值為foo-bar-baz,如果你想按-分割成多個段並遍歷它,可以使用read命令,並且設定 IFS 的值為-:

這裡我們使用read x命令從標準輸入讀取內容,分割後並依次儲存到x y z變數中。其中,$x 為foo$y 為 bar$z 為 baz

另外要留意的一處是here-string操作符<<<,可以很方便地將字串傳遞給命令的標準輸入。在這個例子中,$str的內容傳給 read 命令的標準輸入。

你也可以將分割後的幾個欄位儲存到陣列型別的變數中:

在這裡,-a 選項告訴read命令將分割後的元素儲存到陣列parts中。隨後,你可以通過${parts[0]}${parts[1]}${parts[0]}來訪問陣列的各個元素,或者通過${parts[@]}來訪問所有元素。

8. 逐個字元方式處理字串

這裡我們通過指定-n1引數,讓read命令依次讀入一個字元,類似地,-n2說明每次讀入兩個字元。

9. 將字串中的 foo 替換成 bar

這一行命令用到了引數展開的另外一種形式:${var/find/replace},找到$var變數中的find字串,並將它替換成bar

要想替換所有出現的字串"foo",請使用${var//find/replace}的形式:

10. 檢查字串是否匹配模式

這一行命令是說,如果$file的值匹配*.zip,則執行if語句裡的命令。這種語法下的模式是最簡單的萬用字元(glob pattern)匹配,萬用字元包括* ? [...]。其中,*可以匹配一個或者多個字元,?只能匹配單個字元,[...]能夠匹配任意出現在中括號裡面的字元或者一類字符集。

下面是另外一個例子,用來判斷回答是否匹配 Y 或者 y:er is Y or y:

11. 檢查字串是否匹配某個正規表示式

這一行命令檢查$str是否能夠匹配正規表示式[0-9]+\.[0-9]+,即兩個數字中間包含一個點號。正規表示式的規範可以通過 man 手冊查詢: man 3 regex

12.計算字串的長度

這裡我們又用到了引數展開(也可以叫引數替換)的語法: ${#str},它返回$str變數值的長度。

13. 從字串中提取子串

這一行命令通過子串提取操作,從字串hello world中取到了子串world。子串提取操作的語法格式為${var:offset:length},它的意思是說從變數var中,提取第offset個位置(下標從0開始計算)開始的總共length個數的字元。在我們這個例子中,忽略了length,預設會返回所有剩餘的字元。

下面是另外一個例子,返回$str變數中第7、8位置的兩個字元:

輸出結果為or

14. 轉換成太寫

Bash 中的內建命令 declare 可以用於宣告一個變數,或者設定變數的屬性。在這個例子中,通過指定-u選項,使得變數$var在賦值時,就會自動地將內容轉換成大寫的格式。現在你 echo 它,可以看到所有內容已經變成大寫了:

注意,-u選項也是在 Bash 4 新版本中引入的功能,在低版本下是沒有的。類似地,你還可以使用 Bash 4 提供的另外一種引數展開語法${str^^},也可以將字串轉換成太寫的格式:

15. 轉換成小寫

同上面一條類似,-l選項宣告變數的小寫屬性,使得其值轉換成小寫的格式:

同樣,只有 Bash 4 以及以上的版本才支援-l選項。另外一種方式是使用引數展開語法:

我補充一句,如果是 Bash 4 以下,還是老老實實地用tr命令就可以了。

本文完

相關文章