2019年10月15日 星期二

命令替換 - (())與()還有${}差在哪?

轉自 :  http://wiki.jikexueyuan.com/project/13-questions-of-shell/eight.html

(( ))( )還有${ }差在哪?


我們上一章介紹了( ){ }的不同,這次讓我們擴展一下,看看更多的變化:( ){ }又是啥玩意兒呢?
bash shell$( ) 與 `  ` (反引號)都是用來做 命令替換(command substitution)的。
所謂的命令替換與我們第五章學過的變數替換差不多,都是用來重組命令列:完成 ` ` 或者$( )裡面的 命令,將其結果替換出來,再重組命令列
例如:
$ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)
如此便可方便得到上一個星期天的日期了...^_^
在操作上, 用( )‘‘都無所謂,只是我個人比較喜歡用( ),理由是:
1.       ``(反引號)很容易與''(單引號)搞混亂,尤其對初學者來說。 有時在一些奇怪的字形顯示中,兩種符號是一模一樣的(只取兩點)。 當然了有經驗的朋友還是一眼就能分辨兩者。只是,若能更好的避免混亂, 又何樂而不為呢? ^_^
2.       在多次的複合替換中, ``需要額外的轉義(escape, )處理,而$()則比較直觀。 例如,一個錯誤的使用的例子:
    command1 `command2 `command3` `
原來的本意是要在command2 `command3` , 先將command3替換出來給command2處理, 然後再將command2的處理結果,給command1來處理。 然而真正的結果在命令列中卻是分成了`command2` ``
正確的輸入應該如下:
    command1 `command2 \`command3\` `
要不然換成$()就沒有問題了:
    command1 $(commmand2 $(command3))
只要你喜歡,做多少層的替換都沒有問題~~~^_^
不過,( )並不是沒有弊端的...首先,‘‘ 基本上可用在所有的unixshell中使用,若寫成shellscript,其移植性比較高。而( )並不是每一種shell都能使用,我只能說, 若你用bash2的話,肯定沒問題... ^_^
接下來,再讓我們看看...它其實就是用來做變數替換用的啦。一般情況下,varvar並沒有啥不一樣。但是用{ }會比較精准的界定變數名稱的範圍, 比方說:
$ A=B
$ echo $AB
原本是打算先將A的結果替換出來,然後在其後補一個字母B;但命令列上,真正的結果卻是替換變數名稱為AB的值出來...若使用{ }就沒有問題了:
$ A=B
$ echo ${A}B
$ BB
不過,假如你只看到${}只能用來界定變數名稱的話, 那你就實在太小看bash了。
為了完整起見,我這裡再用一些例子加以說明${}的一些 特異功能: 假設我們定義了一個變數file為:
file=/dir1/dir2/dir3/my.file.txt
我們可以用${}分別替換獲得不同的值:
shell 字串的非貪婪(最小匹配)左刪除
${file#*/}  #其值為:dir1/dir2/dir3/my.file.txt
拿掉第一個/及其左邊的字串,其結果為: dir1/dir2/dir3/my.file.txt 
${file#*.}  #其值為:file.txt
拿掉第一個.及其左邊的字串,其結果為: file.txt 
shell 字串的貪婪(最大匹配)左刪除
${file##*/} #其值為:my.file.txt
拿掉最後一個/及其左邊的字串,其結果為: my.file.txt
${file##*.} #其值為:txt
拿掉最後一個.及其左邊的字串,其結果為: txt
shell 字串的非貪婪(最小匹配)右刪除
${file%/*}  #其值為:/dir1/dir2/dir3
拿掉最後一個/及其右邊的字串,其結果為: /dir1/dir2/dir3
${file%.*}  #其值為:/dir1/dir2/dir3/my.file
拿掉最後一個.及其右邊的字串,其結果為: /dir1/dir2/dir3/my.file
shell 字串的貪婪(最大匹配)右刪除
${file%%/*}  #其值為:其值為空。
拿掉第一個/及其右邊的字串,其結果為: 空串。
${file%%.*}  #其值為:/dir1/dir2/dir3/my
拿掉第一個.及其右邊的字串,其結果為: /dir1/dir2/dir3/my
Tips:
記憶方法:
#是去掉左邊(在鍵盤上#$的左邊);
%是去掉右邊(在鍵盤上%$的右邊);
單個符號是最小匹配;
兩個符號是最大匹配;
shell 字串取子串:
 ${file:0:5} #提取最左邊的5個字元:/dir1
 ${file:5:5} #提取第5個字元及其右邊的5個字元:/dir2
shell 字串取子串的格式:${s:pos:length}, 取字串 s 的子串:從 pos 位置開始的字元(包括該字元)的長度為 length 的的子串; 其中pos為子串的首字元,在 s 中位置; length為子串的長度;
Note: 字串中字元的起始編號為0.
shell 字串變數值的替換
${file/dir/path}  #將第一個dir替換為path/path1/dir2/dir3/my.file.txt
${file//dir/path} #將全部的dir替換為path/path1/path2/path3/my.file.txt
shell 字串變數值的替換格式:
·         首次替換: ${s/src_pattern/dst_pattern} 將字串s中的第一個src_pattern替換為dst_pattern
·         全部替換: ${s//src_pattern/dst_pattern} 將字串s中的所有出現的src_pattern替換為dst_pattern.
${ }還可針對變數的不同狀態(沒設定、空值、非空值)進行賦值
·         ${file-my.file.txt} #如果file沒有設定,則使用 使用my.file.txt作為返回值, 否則返回${file};(空值及非空值時,不作處理。);
·         ${file:-my.file.txt} #如果file沒有設定或者file為空值,均使用my.file.txt作為其返回值,否則,返回{file}.(${file} 為非空值時,不作處理);
·         ${file+my.file.txt} #如果file已設定(為空值或非空值), 則使用my.file.txt作為其返回值,否則不作處理。(未設定時,不作處理);
·         ${file:+my.file.txt} #如果${file}為非空值, 則使用my.file.txt作為其返回值,否則,(未設定或者為空值時)不作處理。
·         ${file=my.file.txt} #如果file為設定,則將file賦值為my.file.txt,同時將file作為其返回值;否則,file已設定(為空值或非空值),則返回{file}
·         ${file:=my.file.txt} #如果file未設定或者file為空值,my.file.txt作為其返回值,同時,將{file}賦值為my.file.txt,否則,(非空值時)不作處理。
·         ${file?my.file.txt} #如果file沒有設定,則將my.file.txt輸出至STDERR, 否側, 已設定(空值與非空值時),不作處理。
·         ${file:?my.file.txt} #若果file未設定或者為空值,則將my.file.txt輸出至STDERR,否則, 非空值時,不作任何處理。
Tips:
以上的理解在於,你一定要分清楚,unsetnull以及non-null這三種狀態的賦值; 一般而言,與null有關,若不帶:, null不受影響; 若帶 :, 則連null值也受影響。
計算 shell 字串變數的長度:${#var}
 ${#file}  #其值為27, 因為/dir1/dir2/dir3/my.file.txt剛好為27個字元。
bash 陣列(array)的處理方法
接下來,為大家介紹一下bash的陣列(array)的處理方法。 一般而言A="a b c def" 這樣的變數只是將$A替換為一個字串, 但是改為 A=(a b c def), 則是將$A定義為陣列....
陣列替換方法可參考如下方法
${A[@]} #方法一
${A[*]} #方法二
以上兩種方法均可以得到:a b c def, 即陣列的全部元素。
訪問陣列的成員
${A[0]}
其中,${A[0]}可得到a, 即陣列A的第一個元素, 而 ${A[1]}則為陣列A的第二元素,依次類推。
陣列的 length
${#A[@]} #方法一
${#A[*]} #方法二
以上兩種方法均可以得到陣列的長度: 4, 即陣列的所有元素的個數。
回憶一下,針對字串的長度計算,使用${#str_var}; 我們同樣可以將該方法應用于陣列的成員:
${#A[0]}
其中,${#A[0]}可以得到:1,即陣列A的第一個元素(a)的長度; 同理,${#A[3]}可以得到: 3, 即陣列A的第4個元素(def)的長度。
陣列元素的重新賦值
A[3]=xyz
將陣列A的第四個元素重新定義為 xyz
Tips:
諸如此類的...
能夠善用bash(){}可以大大提高及 簡化shell在變數上的處理能力哦~~~^_^
$(())作用
好了,最後為大家介紹$(())的用途吧: $(())是用來作整數運算的
bash中, $(())的整數運算子號大致有這些:
·         +- * / #分別為"加、減、乘、除"
·         % #餘數運算,(模數運算)
·         & | ^ ! #分別為"ANDORXORNOT"運算。
例如:
$ a=5; b=7; c=2;
$ echo $(( a + b * c ))
19
$ echo $(( (a + b)/c ))
6
$ echo $(( (a * b) % c ))
1
$(())中的變數名稱, 可以在其前面加 $符號來替換, 也可以不用,如: $(( $a + $b * $c )) 也可以得到19的結果。
此外,$(())還可作不同進制(如二進位、八進制、十六進位)的運算, 只是輸出結果均為十進位的。
echo $(( 16#2a )) #輸出結果為:42(16進制的2a)
以一個實用的例子來看看吧 : 假如當前的umask022,那麼新建檔的許可權即為:
$ umask 022
$ echo "obase=8; $(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
644
事實上,單純用(())也可以重定義變數值,或作 testing
a=5; ((a++)) #可將$a 重定義為6
a=5; ((a--)) #可將$a 重定義為4
a=5; b=7; ((a< b)) #會得到0 (true)返回值。
常見的用於(())的測試符號有如下這些:
符號
符號名稱
< 
小於號
> 
大於號
<=
小於或等於
>=
大於或等於
==
等於
!=
不等於
Note:
使用(())作整數測試時, 請不要跟[]的整數測試搞混亂了。
更多的測試,我們將于第10章為大家介紹。
怎樣? 好玩吧... ^_^
okay,這次暫時說這麼多...
上面的介紹,並沒有詳列每一種可用的狀態, 更多的,就請讀者參考手冊檔(man)...

沒有留言:

張貼留言