在 bash 中,一般使用 ${parameter}
表達式來獲取 parameter 變量的值,這是一種參數擴展 (parameter expansion)。
Bash 還提供了其餘形式的參數擴展,能夠對變量值作一些處理,起到操做字符串的效果。例如:正則表達式
${parameter#word}
從 parameter 變量值的開頭日後刪除匹配 word 的部分,保留後面剩餘內容。${parameter%word}
從 parameter 變量值的末尾往前刪除匹配 word 的部分,保留前面剩餘內容。注意:這些表達式都不會修改 parameter 自身的變量值,它們只是基於 parameter 變量值擴展獲得新的值。
若是要保存這些值,須要賦值給具體的變量。數組
查看 man bash 的 Parameter Expansion 小節,就能看到相關說明。具體舉例說明以下。bash
${parameter#word}
和 ${parameter##word}
查看 man bash 對 ${parameter#word}
和 ${parameter##word}
的說明以下:app
Romove matching prefix pattern.
The work is expanded to produce a pattern just as in pathname expansion.If the pattern matches the beginning of the value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the '#' case) or the longest matching pattern (the '##' case) deleted. 函數
If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list.
If parameter is an array variable subscripted with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.code
即,${parameter#word}
在 parameter 變量值中,從頭開始匹配 word 模式對應的內容。
若是匹配,則刪除最短匹配部分並返回變量剩餘部份內容。
後面會具體舉例說明,方便理解。ip
而 ${parameter##word}
是在 parameter 變量值中,從頭開始匹配 word 模式對應的內容。
若是匹配,則刪除最長匹配部分並返回變量剩餘部份內容。rem
上面所說的 "最短匹配"、"最長匹配" 主要是針對有多個匹配的狀況。
若是 parameter 變量值中有多個地方匹配 word 模式,則 "最短匹配" 是指在第一次匹配時就中止匹配,並返回剩餘的內容。
而 "最長匹配" 會一直匹配到最後一次匹配爲止,才返回剩餘的內容。字符串
這裏的 word 模式可使用通配符進行擴展,注意不是用正則表達式。string
注意:上面所說的 「從頭開始匹配」 是指把 parameter 變量值跟 word 模式從頭開始比較,而不是在 parameter 變量值中任意匹配 word 模式。
具體舉例說明以下:
$ value="This/is/a/test/string" $ echo ${value#This} /is/a/test/string $ echo ${value#test} This/is/a/test/string $ echo ${value#*test} /string
上面先定義了一個 value 變量,而後獲取 ${value#This}
的值。
這個參數擴展表示在 value 變量值中從頭開始匹配 "This" 字符串。
若是匹配,則刪除 "This" 字符串,返回 value 變量值剩餘部份內容。
這裏可以匹配,因此去掉了 "This" 字符串,打印 "/is/a/test/string"。
可是 echo ${value#test}
的打印結果跟 value 變量值徹底同樣。
即沒有匹配到中間的 "test" 字符串,最短匹配爲空,沒有刪除任何字符串。
使用 ${value#*test}
才能匹配到 value 變量值中間的 "test" 字符串,並刪除全部匹配的內容,打印 "/string"。
這裏用 *
通配符匹配在 "test" 前面的任意字符,從而匹配 value 變量值開頭的部分。
注意:${parameter##word}
是操做 parameter 變量值,而不是操做 "parameter" 字符串。
因此想要過濾某個字符串的內容,須要先把字符串賦值給某個變量,再用變量名來進行參數擴展。
直接把字符串內容寫在大括號 {}
裏面不起做用,即便用引號把字符串括起來也不行。
具體舉例說明以下:
$ echo ${This/is/a/test/string#This} # 執行不會報錯,可是輸出爲空 $ echo ${"This/is/a/test/string"#This} # 添加引號會執行報錯 -bash: ${"This/is/a/test/string"#This}: bad substitution $ echo ${'This/is/a/test/string'#This} -bash: ${'This/is/a/test/string'#This}: bad substitution
能夠看到,這裏的四個表達式都不能直接處理字符串自身的內容。
${parameter##word}
的用法跟 ${parameter#word}
相似,也是刪除匹配的內容,返回剩餘的內容。
區別在於,${parameter#word}
是匹配到第一個就中止。
而 ${parameter##word}
是匹配到最後一個才中止。
以上面的 value 變量爲例,具體說明以下:
$ echo ${value#*is} /is/a/test/string $ echo ${value##*is} /a/test/string
能夠看到,echo ${value##*is}
打印的是 "/is/a/test/string"。
所給的 *is
匹配到 "This" 這個字符串,沒有再匹配後面的 "is" 字符串,因此只刪除了 "This" 字符串。
而 echo ${value##*is}
打印的是 "/a/test/string"。
它先匹配到 "This",但仍是繼續日後匹配,最後一個匹配是 "is" 字符串,因此刪掉了 "This/is" 字符串。
再次強調,上面說的 "匹配" 是從頭開始匹配,而不是部分匹配。
它是要求 word 模式擴展以後獲得的字符串從頭開始符合 parameter 變量值的內容,而不是在 parameter 變量值裏面查找 word 模式。
例以下面的例子:
$ value="This/is/a/test/string./This/is/a/new/test" $ echo ${value##*This} /is/a/new/test. $ echo ${value##This} /is/a/test/string./This/is/a/new/test
能夠看到,value 變量值有兩個 "This" 字符串。 echo ${value##*This}
匹配到了最後一個 "This" 字符串。
可是 echo ${value##This}
仍是隻匹配到開頭的 "This" 字符串。
它不是在 value 變量值裏面查找 "This" 這個子字符串,而是把 "This" 字符串和 value 變量值從頭開始、逐個字符進行匹配。
而在 echo ${value##*This}
表達式中,*This
通過通配符擴展後,在 value 變量值中有幾種形式的匹配。
例如從頭匹配到 "This" 字符串、匹配到 "This/is/a/test/string./This" 字符串。
去掉最長匹配後,打印爲 "/is/a/new/test."。
注意體會其中的區別。
注意:在 bash 的參數擴展中,數字屬於 positional parameters,能夠用數字來引用傳入腳本或者函數的參數。
用在當前表達式時,就表示對傳入的參數值進行處理。
例如 $1
對應傳入的第一個參數,那麼 ${1##pattern}
表示在傳入第一個參數值中匹配 pattern 模式,並進行處理。
具體舉例以下:
$ function param_tail() { echo ${1##*test}; } $ param_tail "This is a test string." string.
能夠看到,param_tail 函數使用 ${1##*test}
在傳入的第一個參數中匹配 "test" 字符串、以及它前面的任意字符。
若是可以匹配,去掉匹配的內容,只保留後面的部分。
若是所給的 parameter 是一個數組變量且所給下標是 @
或者 *
,那麼會對每個數組元素都進行匹配刪除操做,最後獲得的擴展結果是合成後的列表。
當須要對多個字符串進行相同處理時,就能夠把它們放到一個數組裏面,而後使用這兩個表達式來進行處理。
下面的例子從多個文件路徑中過濾出各自的文件名,去掉了目錄路徑部分:
$ arrays=("src/lib/utils.c" "src/main/main.c" "include/lib/utils.h") $ echo ${arrays[@]##*/} utils.c main.c utils.h
能夠看到,${arrays[@]##*/}
表達式使用 arrays[@]
來指定操做 arrays 數組的每個元素。
用 */
來匹配目錄路徑的 /
字符,且在該字符前面能夠有任意多的其餘字符。
用 ##
指定進行最長匹配,從而會匹配到最後一個 /
字符爲止。
最終,該表達式刪除匹配的部分,返回剩餘的內容,也就是返回文件名自身。
${parameter%word}
和 ${parameter%%word}
查看 man bash 對 ${parameter%word}
和 ${parameter%%word}
的說明以下:
Remove matching suffix pattern.
The word is expanded to produce a pattern just as in pathname expansion.If the pattern matches a trailing portion of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the '%' case) or the longest matching pattern (the '%%' case) deleted.
If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list.
If parameter is an array variable subscripted with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.
即,${parameter%word}
匹配 parameter 變量值的後綴部分,從後往前匹配 word 模式對應的內容。
若是匹配,則刪除最短匹配部分並返回變量剩餘部份內容。
所謂的 "最短匹配" 是指從 parameter 變量值的末尾開始,從後往前匹配,一匹配到就結束匹配。
後面會舉例說明,方便理解。
而 ${parameter%%word}
匹配 parameter 變量值的後綴部分,從後往前匹配 word 模式對應的內容。
若是匹配,則刪除最長匹配部分並返回變量剩餘部份內容。
所謂的 "最長匹配" 是指從 parameter 變量值的末尾開始,從後往前匹配,一直匹配到最後一次匹配爲止。
這裏的 word 模式可使用通配符進行擴展,注意不是用正則表達式。
前面說明的 ${parameter#word}
是從變量值中刪除匹配的前綴部分,保留後面剩餘的內容。
而 ${parameter%word}
相反,是從變量值中刪除匹配的後綴部分,保留後面剩餘的內容。
具體舉例說明以下:
$ value="This/is/a/test/string./This/is/a/new/test" $ echo ${value%test} This/is/a/test/string./This/is/a/new/ $ echo ${value%%test} This/is/a/test/string./This/is/a/new/ $ echo ${value%%test*} This/is/a/ $ echo ${value%is*} This/is/a/test/string./This/ $ echo ${value#*is} /is/a/test/string./This/is/a/new/test
能夠看到,${value%test}
在 value 變量值中,從後往前匹配到 "test" 字符串,刪除匹配的內容,獲取到前面剩餘的部分。
而 ${value%%test}
獲取到的值跟 ${value%test}
同樣。
由於該表達式是從末尾開始逐字匹配,因此不會往前匹配到第二個 "test" 字符串,只匹配了末尾的 "test" 字符串。
若是要往前匹配到第二個 "test" 字符串,可使用 *
通配符。 ${value%%test*}
表示從後往前匹配,直到最後一個 "test" 模式才中止。
因爲 ${parameter%word}
表達式是從後往前匹配,因此 *
通配符要寫在 word 模式的後面,才能匹配到中間的內容。
如 ${value%is*}
的輸出結果所示。
而 ${parameter#word}
是從前日後匹配,因此 *
通配符要寫在 word 模式的前面,才能匹配到中間的內容。
如 ${value#*is}
的輸出結果所示。