1. 程式人生 > 其它 >Shell 引數擴充套件及各類括號在 Shell 程式設計中的應用

Shell 引數擴充套件及各類括號在 Shell 程式設計中的應用

今天看有人總結了 shell 下的引數擴充套件,但不是很全,恰好以前整理過放在百度空間,但百度空間目前半死不活的情況下對 Google 非常不友好,索性一併轉過來方便查閱。

1、bash 中的大括號引數擴充套件(Parameter Expansion)

假設我們定義了一個變數為:

file=/dir1/dir2/dir3/my.file.txt

1.1 bash 下的 split 取“陣列”的首、尾:

${file#*/}:拿掉第一條 / 及其左邊的字串:dir1/dir2/dir3/my.file.txt
${file##*/}:拿掉最後一條 / 及其左邊的字串:my.file.txt
${file#*.}:拿掉第一個 .  及其左邊的字串:file.txt
${file##*.}:拿掉最後一個 .  及其左邊的字串:txt
${file%/*}:拿掉最後條 / 及其右邊的字串:/dir1/dir2/dir3
${file%%/*}:拿掉第一條 / 及其右邊的字串:(空值)
${file%.*}:拿掉最後一個 .  及其右邊的字串:/dir1/dir2/dir3/my.file
${file%%.*}:拿掉第一個 .  及其右邊的字串:/dir1/dir2/dir3/my

Tips:

記憶的方法為: # 是去掉左邊(在鍵盤上 # 在 $ 之左邊) % 是去掉右邊(在鍵盤上 % 在 $ 之右邊) 單一符號是最小匹配﹔兩個符號是最大匹配(類似貪婪匹配)。

1.2 bash 下的 substring 按字元位置、長度擷取

${file:0:5}:提取最左邊的 5 個位元組:/dir1
${file:5:5}:提取第 5 個位元組右邊的連續 5 個位元組:/dir2
${#file}:計算出字串的長度,/dir1/dir2/dir3/my.file.txt 字串長度 27
${file: -4}:提取最後四個字串(空格是為了避免衝突,注意不同於echo ${file:-4},也可以用(-4)代替空格),類似用法(提取前四個字元) ${file:0:4}

1.3 bash 下的 replace 與 replaceAll

我們也可以對變數值裡的字串作替換:

${file/dir/path}:將第一個 dir 提換為 path:/path1/dir2/dir3/my.file.txt
${file//dir/path}:將全部 dir 提換為 path:/path1/path2/path3/my.file.txt

1.4 bash 下的變數空值檢測與初始化

利用 ${ } 還可針對不同的變數狀態賦值(沒設定、空值、非空值):

${file-my.file.txt} :假如 $file 沒有設定,則使用 my.file.txt 作傳回值。(空值及非空值時不作處理)
${file:-my.file.txt} :假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作處理)
${file+my.file.txt} :假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作處理)
${file:+my.file.txt} :若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作處理)
${file=my.file.txt} :若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。 (空值及非空值時不作處理)
${file:=my.file.txt} :若 $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: 以上的理解在於, 你一定要分清楚 unset 與 null 及 non-null 這三種賦值狀態. 一般而言, : 與 null 有關, 若不帶 : 的話, null 不受影響, 若帶 : 則連 null 也受影響。 而 - 和 = 的區別在於是否把傳回值賦給引用變數,例如:

${parameter:-word}     word is only substituted.
${parameter:=word}     word is substituted and assigned to parameter.
root@localhost ~ $ echo "$var"


root@localhost ~ $ echo "${var:-hello}"
hello
root@localhost ~ $ echo "$var"


root@localhost ~ $ echo "${var:=hello}"
hello
root@localhost ~ $ echo "$var"
hello

1.5 bash 下的陣列和關聯陣列

Bash4中可以使用兩種容器。 一種是陣列,另一種是關聯陣列,類似於其他語言中的Map/Hash/Dict。 宣告陣列的常用語法: declare -a ARY或者ARY=(1 2 3) 宣告關聯陣列的唯一語法: declare -A MAP(bash4以下不支援) 賦值的語法: 直接ARY[N]=VALUE,N可以是數字索引也可以是鍵。關聯陣列可以使用MAP=([x]=a [y]=b)進行多項賦值,注意這是賦值的語句而不是宣告。 親測陣列中的索引不一定要按順序來,你可以先給2和3上的元素賦值。(同樣算是弱型別的Javascript也支援這種無厘頭賦值,這算通病麼?)

往現有陣列批量新增元素:
ARY+=(a b c)
MAP+=([a]=1 [b]=2)
取值:
${ARY[INDEX]}
${MAP[KEY]}
注意花括號的使用
${A[@]} 展開成所有的變數,而獲取陣列長度使用 ${#A[@]}
切片:
${ARY[@]:N:M} N是offset而M是length
返回索引,相當於keys():
${!MAP[@]}
試試下面的程式碼:
declare -a ARY
declare -A MAP
MAP+=([a]=1 [b]=2)
ARY+=(a b c)

echo ${ARY[1]}
echo ${MAP[a]}
echo "${ARY[@]}"
echo "${MAP[@]}"
echo "${ARY[@]:0:1}"
echo ${#ARY[@]}
echo "${!MAP[@]}"

ARY[4]=a
echo ${ARY[@]}
echo ${ARY[3]}

1.6 bash 下的大小寫變換

HI=HellO

echo "$HI" # HellO
echo ${HI^} # HellO
echo ${HI^^} # HELLO
echo ${HI,} # hellO
echo ${HI,,} # hello
echo ${HI~} # hellO
echo ${HI~~} #hELLo
^大寫,,小寫, ~大小寫切換
重複一次只修改首字母,重複兩次則應用於所有字母。

混著用會怎樣?
echo ${HI^,^} # HellO
看來是不行的×_×

2、各類括號在 shell/bash 程式設計中的應用

上面應該見識到了 shell 中大括號的強大功能,其實 shell 下有很多種括號,不像其它高階語言括號只起到語法和意義的作用,而 shell 下的每種括號除了語法、語義的作用之外,還對 shell 程式設計起到了功能上的擴充套件。

2.1 () 在子shell中執行
    (a=1);echo $a,結果是空,因為a=1不是在當前shell中執行的(a=1);(echo $a)也是空的。
    小技巧:(cd $path, do something) 可以讓不切換當前目錄而在其它目錄乾點別的事兒~
    () 還有個功能是陣列的賦值:比如a=(1 3 5),那麼${a[0]}=1;${a[1]}=3;${a[2]}=5,需要注意的是,下標是從0開始的。
    
2.2 (()) 表示式計算
    a=1;((a++));echo $a,這時a就是2了。
    
2.3 <() 和 >() 程序代入,可以把命令的執行結果當成檔案一樣讀入
    比如comm前一般需要sort,那就可以這樣comm <(sort 1.lst) <(sort 2.lst)
    或者是paste <(cut -t2 file1) <(cut -t1 file1),和管道差不多,但是支援多個輸入。

2.4 $() $(cmd) 執行cmd的結果,
    比如cmd是echo ls,那麼就是執行ls,比如file $(which bash),which bash的結果是/bin/bash,
    所以file $(which bash)等於file /bin/bash。如果你$(ls),而且你的當前目錄下只有a b兩個檔案,
    那麼就是執行a b,然後系統會提示,命令沒找到。$() 基本和 `` 等價。
    
2.5 $(()) 表示式擴充套件,
    和(())很相似,但是這個是有點不同,$(())不能直接$((b++)),例如:b=1;echo $((++b))
    這時b等於2,顯示的也是2,b=1; echo $((b++))這時b等於2,顯示的是1.
    
2.6 [] 和 [[]],[] 就是 test,[]和[[]]都是條件表示式,不過[[]]有比[]高的容錯性,
    如果a為空,那麼[ $a -eq 0 ]會報錯,但是[[ $a -eq 0 ]]不會,所以一般都會使用[[]]或者是
    [ "$a" -eq 0 ],[[]]支援的功能也比 [] 多,比如[[ aaa =~ a{3} ]],[] 還有一種用途,
    如果你的當前目錄下有a1-a9九個檔案,你可以用a[1-9]來替代這九個檔案。
    有點需要注意,你不能用a[1-20]來代替a1- a20,必須要a[1-9] a1[0-9] a20。
    但是需要注意的是 [[]] 數字進位制轉換的坑~
    
2.7 $[] 是 $(()) 的過去形式,現在已經不建議使用。

2.8 {n..m} {1..30} 就是1-30,或者是/{,s}bin/表示/bin/和/sbin/,ab{c,d,e}表示abc、abd、abe,
    小技巧:檔案備份:cp a.sh{,.bak}
    而 { cmd1; cmd2; } 的作用是定義一個命令組,一般用在單行的條件表示式中:
    [[ 1 -eq 2 ]] && echo True || { echo False; echo "Program will exit!"; }
    其實 shell 函式的語法也是它的變體:
    a(){ i=$1; echo $((i++)); echo $((++i)); } && a 1

2.9 ${} 變數的Parameter Expansion,
    用法很多,最基本的 ${var}1,防止變數擴充套件衝突,具體可以檢視man bash。
    或者參考我之前的博文連結:http://hi.baidu.com/leejun_2005/blog/item/ebfee11a4177ddc1ac6e751d.html

3、bash命令執行流程:

執行分為四大步驟:輸入、解析、擴充套件和執行。

4、Refer:

[1] shell十三問之大括號引數擴充套件(Parameter Expansion)

http://hi.baidu.com/leejun_2005/item/138c09343aaddff6e6bb7a49

[2] shell 十三問? 

http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=218853&page=7#

[3] shell/bash程式設計中各類括號的應用

http://hi.baidu.com/leejun_2005/item/6f9eb7345e5f4f302f20c453

[4] Bash Hackers Wiki Frontpage » Syntax » Parameter expansion

http://wiki.bash-hackers.org/syntax/pe

[5] 玩轉Bash變數

http://segmentfault.com/blog/spacewander/1190000002539169

[6] Bash快速入門指南

http://blog.jobbole.com/85183/

[7] SHELL(bash)指令碼程式設計六:執行流程

https://segmentfault.com/a/1190000008215772