序
Shell腳本實(shí)在是太靈活了,相比標(biāo)準(zhǔn)的Java、C左敌、C++ 等,它不過(guò)是一些現(xiàn)有命令的堆疊,這是他的優(yōu)勢(shì)也是他的劣勢(shì),太靈活導(dǎo)致不容易寫(xiě)規(guī)范。本人在寫(xiě)Shell腳本的過(guò)程中形成了自己一些規(guī)范,這些規(guī)范仍在實(shí)踐中,在此分享出來(lái).
Shell介紹
Shell中文譯名叫 殼(ke,又讀qiao) 外殼的意思,有殼就要有核.核指的是內(nèi)核,內(nèi)核即操作系統(tǒng)內(nèi)的核心代碼,內(nèi)核是操作系統(tǒng)對(duì)計(jì)算機(jī)硬件資源(例如顯示器绽淘、硬盤(pán)炸站、內(nèi)存等等)進(jìn)行調(diào)度的唯一通道,也就是說(shuō)所有對(duì)計(jì)算機(jī)發(fā)出的指令,例如使蜂鳴器鳴響、點(diǎn)亮鍵盤(pán)背光吁讨、復(fù)制粘貼等等都需要經(jīng)過(guò)內(nèi)核來(lái)操作才行,這無(wú)論如何你是跨越不過(guò)去的髓迎。
首先我們要明白,Shell是一種解釋型語(yǔ)言,需要Shell解釋器來(lái)解釋執(zhí)行,每讀一行就執(zhí)行一行。歷史上出現(xiàn)過(guò)很多Shell解釋器,例如 sh,bash,csh,zsh 等等,不同的Shell有不同的詞法和語(yǔ)法,就好比不同的方言建丧。用不同的Shell其實(shí)指的僅僅是更換了腳本解釋器而已排龄。要注意的是有些命令在不同的解釋器中會(huì)有不同的表現(xiàn),這也是為什么我們的腳本一移植到別人電腦上就不可用的原因之一。
坑一:echo 在 zsh 和其他 Shell解釋器中對(duì)特殊字符轉(zhuǎn)義的輸出就不同翎朱。
chenshang@chenshangMacBook-Pro:~$ bash
chenshang@chenshangMacBook-Pro:~$ echo \\
\
chenshang@chenshangMacBook-Pro:~$ echo \\\\
\\
chenshang@chenshangMacBook-Pro:~$ zsh
chenshangMacBook-Pro% echo \\
\
chenshangMacBook-Pro% echo \\\\
\
坑二: 數(shù)組使用方式不同
chenshang@chenshangMacBook-Pro:~$ bash
chenshang@chenshangMacBook-Pro:~$ a=(1 2 3)
chenshang@chenshangMacBook-Pro:~$ echo ${a}
1
chenshang@chenshangMacBook-Pro:~$ zsh
chenshangMacBook-Pro% a=(1 2 3)
chenshangMacBook-Pro% echo ${a}
1 2 3
chenshangMacBook-Pro%
總之,Shell腳本中的坑很多,林林總總,寫(xiě)腳本的時(shí)候一定要小心,否則腳本的移植性堪憂橄维。這也就是為什么Shell不適合開(kāi)發(fā)大型應(yīng)用的原因之一。但輔助開(kāi)發(fā)還是綽綽有余的拴曲。尤其是在運(yùn)維服務(wù)器和對(duì)文本處理的過(guò)程中,與linux的親和性讓它占盡了優(yōu)勢(shì)争舞。
本Shell規(guī)約是以bash為標(biāo)準(zhǔn),在Mac OS 10.14上進(jìn)行驗(yàn)證。
chenshang@chenshangMacBook-Pro:~$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14
chenshang@chenshangMacBo ok-Pro:~$ uname -a
BuildVersion: 18A391Darwin chenshangMacBook-Pro.local 18.0.0 Darwin Kernel Version 18.0.0: Wed Aug 22 20:13:40 PDT 2018; root:xnu-4903.201.2~1/RELEASE_X86_64 x86_64
chenshang@chenshangMacBook-Pro:~$ echo $0
-bash
chenshang@chenshangMacBook-Pro:~$ echo ${SHELL}
/usr/local/bin/bash
chenshang@chenshangMacBook-Pro:~$ bash -version
GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin18.2.0)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Shell既是一種腳本編程語(yǔ)言,也是一個(gè)連接內(nèi)核和用戶的軟件.既然他是一門(mén)語(yǔ)言就免不了兩大要素:詞法和語(yǔ)法,首先有哪些詞匯(保留詞澈灼、關(guān)鍵字),詞匯有哪些分類(數(shù)據(jù)類型),其次這些詞匯如何表達(dá)語(yǔ)意(也就是語(yǔ)法)竞川。Shell編程指的并不是編寫(xiě)這個(gè)工具,而是指利用現(xiàn)有的Shell工具進(jìn)行編程,寫(xiě)出來(lái)的程序是輕量級(jí)的腳本,我們叫做Shell腳本。Shell的語(yǔ)法是從C繼承過(guò)來(lái)的,因此我們?cè)趯?xiě)Shell腳本的時(shí)候往往能看到C語(yǔ)言的影子。因?yàn)槌醮鶸nix內(nèi)核中的Shell解釋器最主要的兩個(gè)貢獻(xiàn)者是肯湯普森和丹尼斯里奇,而丹尼斯里奇是C語(yǔ)言的發(fā)明者,肯湯普森則用C語(yǔ)言重寫(xiě)了之前的Unix內(nèi)核流译。
基礎(chǔ)語(yǔ)法
說(shuō)明
寫(xiě)Shell腳本考驗(yàn)是的你對(duì)各個(gè)命令或工具使用的熟練程度≌甙蹋基本命令必須要熟練掌握,常用命令要知道基本功能和基本參數(shù),生僻命令只需要知道大概即可,要學(xué)會(huì)使用man手冊(cè)查看命令的幫助文檔福澡。對(duì)于一些命令不必死記硬背,用的時(shí)候查一下解決問(wèn)題即可。
基本命令例如 /bin 目錄下的
chenshang@chenshangMacBook-Pro:~$ ls /bin/
[ chmod date echo hostname launchctl ls pax rm sleep tcsh wait4path
bash cp dd ed kill link mkdir ps rmdir stty test zsh
cat csh df expr ksh ln mv pwd sh sync unlink
常用命令和工具
sed驹马、awk革砸、grep、tr糯累、column算利、ssh、scp泳姐、expect效拭、ps、top胖秒、htop缎患、tree、pstree阎肝、curl挤渔、wget、java相關(guān)的工具
本Shell規(guī)約規(guī)定
【強(qiáng)制】Linux 工具的使用不必死記硬背,不必死記硬背,不必死記硬背,即查即用即可风题。
運(yùn)行方式
既可以在命令行交互的運(yùn)行,又可以將指令固化到文件中執(zhí)行
chenshang@chenshangMacBook-Pro:~$ cat test.sh
#!/bin/zsh
echo \\\\
array=(1 2 3)
echo "${array}"
chenshang@chenshangMacBook-Pro:~$ sh test.sh
\
1 2 3
chenshang@chenshangMacBook-Pro:~$ zsh test.sh
\\
1
chenshang@chenshangMacBook-Pro:~$ chmod u+x test.sh
chenshang@chenshangMacBook-Pro:~$ ll test.sh
-rw-rw-rw- 1 chenshang staff 40 Feb 18 17:32 test.sh
chenshang@chenshangMacBook-Pro:~$ ./test.sh
\
1 2 3
chenshang@chenshangMacBook-Pro:~$
關(guān)于首行
【推薦】#!/usr/bin/env bash
說(shuō)明:腳本用env啟動(dòng)的原因,是因?yàn)槟_本解釋器在linux中可能被安裝于不同的目錄,env可以在系統(tǒng)的PATH目錄中查找判导。
我們往往看到大多數(shù)Shell腳本的第一行是 #!/bin/bash 這句話,當(dāng)然也有 #!/bin/sh、#!/usr/bin/bash,這幾種寫(xiě)法也都算是正確, 當(dāng)然還有一些野路子的寫(xiě)法,為了避免誤導(dǎo)這里就不示例了沛硅。本Shell規(guī)約并不推薦使用上面的任何一種,而是使用 #!/usr/bin/env bash 這種眼刃。
首行關(guān)系到運(yùn)行腳本的時(shí)候究竟使用哪種Shell解釋器。這也說(shuō)明Shell是一種解釋性語(yǔ)言,腳本從上到下每讀一行就執(zhí)行一行,在遇到第一行是 #!/bin/bash 的時(shí)候就會(huì)加載 bash 相關(guān)的環(huán)境,在遇到 #!/bin/sh 就會(huì)加載 sh 相關(guān)的環(huán)境,避免在執(zhí)行腳本的時(shí)候遇到意想不到的錯(cuò)誤摇肌。但一開(kāi)始我并不知道我電腦上安裝了哪些Shell,默認(rèn)使用的又是哪一個(gè)Shell,我腳本移植到別人的計(jì)算機(jī)上執(zhí)行,我更不可能知道別人的計(jì)算機(jī)是Ubuntu還是Arch或是Centos鸟整。為了提高程序的移植性,本Shell規(guī)約規(guī)定使用 #!/usr/bin/env bash, #!/usr/bin/env bash 會(huì)自己判斷使用的Shell是目錄在哪,并加載相應(yīng)的環(huán)境變量。
我們看一下下面一段腳本,在改變第一行頭部的時(shí)候,shellcheck給出的建議是什么 $ cat test.sh
echo \\\\
array=(1 2 3)
echo "${array}"
不使用首行的時(shí)候
In test.sh line 1:
echo \\\\
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.
In test.sh line 3:
echo "${array}"
^------^ SC2128: Expanding an array without an index only gives the first element.
For more information:
https://www.shellcheck.net/wiki/SC2148 -- Tips depend on target shell and y...
https://www.shellcheck.net/wiki/SC2128 -- Expanding an array without an ind...
使用 #!/bin/bash 或 #!/usr/bin/env bash
In test.sh line 4:
echo "${array}"
^------^ SC2128: Expanding an array without an index only gives the first element.
For more information:
https://www.shellcheck.net/wiki/SC2128 -- Expanding an array without an ind...
使用 #!/bin/zsh
In test.sh line 1:
#!/bin/zsh
^-- SC1071: ShellCheck only supports sh/bash/dash/ksh scripts. Sorry!
For more information:
https://www.shellcheck.net/wiki/SC1071 -- ShellCheck only supports sh/bash/...
使用 #!/bin/sh
In test.sh line 3:
array=(1 2 3)
^-----^ SC2039: In POSIX sh, arrays are undefined.
In test.sh line 4:
echo "${array}"
^------^ SC2128: Expanding an array without an index only gives the first element.
For more information:
https://www.shellcheck.net/wiki/SC2039 -- In POSIX sh, arrays are undefined.
https://www.shellcheck.net/wiki/SC2128 -- Expanding an array without an ind...
這一行不寫(xiě)大多數(shù)時(shí)候我們運(yùn)行腳本的時(shí)候也沒(méi)有問(wèn)題,但在使用Shellcheck進(jìn)行靜態(tài)代碼檢查的時(shí)候,會(huì)提示
^-- SC2148: Tips depend on target Shell and yours is unknown. Add a shebang.
如果使用Intellij IDEA 也會(huì)提示 add shebang line
當(dāng)你點(diǎn)擊 Add shebangline
的時(shí)候它會(huì)自動(dòng)添加 #!/usr/bin/env bash
,這也是為什么本Shell規(guī)約推薦使用 #!/usr/bin/env bash
的原因之一
shebang 維基百科
在計(jì)算機(jī)科學(xué)中,Shebang(也稱為 Hashbang )是一個(gè)由井號(hào)和嘆號(hào)構(gòu)成的字符序列 #! ,其出現(xiàn)在文本文件的第一行的前兩個(gè)字符朦蕴。 在文件中存在 Shebang 的情況下,類 Unix 操作系統(tǒng)的程序載入器會(huì)分析 Shebang 后的內(nèi)容,將這些內(nèi)容作為解釋器指令,并調(diào)用該指令,并將載有 Shebang 的文件路徑作為該解釋器的參數(shù)[1]篮条。
例如,以指令
#!/bin/sh
開(kāi)頭的文件在執(zhí)行時(shí)會(huì)實(shí)際調(diào)用/bin/sh
程序(通常是 Bourne Shell 或兼容的 Shell,例如 bash、dash 等)來(lái)執(zhí)行吩抓。這行內(nèi)容也是 Shell 腳本的標(biāo)準(zhǔn)起始行涉茧。
數(shù)據(jù)類型
字符串
shell語(yǔ)言是一門(mén)弱類型語(yǔ)言,無(wú)論輸入的是字符串還是數(shù)字,shell都是按照字符串類型來(lái)進(jìn)行存儲(chǔ)的,具體屬于什么數(shù)據(jù)類型,shell會(huì)根據(jù)上下文進(jìn)行確定,字符串可以用單引號(hào),也可以用雙引號(hào),也可以不用引號(hào)。單雙引號(hào)的區(qū)別跟PHP類似疹娶。
區(qū)別
- 單引號(hào)里的任何字符都會(huì)原樣輸出,單引號(hào)字符串中的變量是無(wú)效的伴栓;
- 單引號(hào)字串中不能出現(xiàn)單獨(dú)一個(gè)的單引號(hào)(對(duì)單引號(hào)使用轉(zhuǎn)義符后也不行),但可成對(duì)出現(xiàn),作為字符串拼接使用。
- 雙引號(hào)里可以有變量(這在編程語(yǔ)言里面叫字符串插值)和轉(zhuǎn)義字符
Shell數(shù)組
定義數(shù)組
bash支持一維數(shù)組(不支持多維數(shù)組),并且沒(méi)有限定數(shù)組的大小。類似于 C 語(yǔ)言,數(shù)組元素的下標(biāo)由 0 開(kāi)始編號(hào)钳垮。獲取數(shù)組中的元素要利用下標(biāo),下標(biāo)可以是整數(shù)或算術(shù)表達(dá)式,其值應(yīng)大于或等于 0惑淳。在 Shell 中,用括號(hào)來(lái)表示數(shù)組,數(shù)組元素用"空格"符號(hào)分割開(kāi)。
定義數(shù)組的一般形式為:數(shù)組名=(值1 值2 ... 值n).例如:
array=(value0 value1 value2 value3)
或者
array=(
value0
value1
value2
value3
)
還可以單獨(dú)定義數(shù)組的各個(gè)分量:
array[0]=value0
array[1]=value1
array[n]=valueN
可以不使用連續(xù)的下標(biāo),而且下標(biāo)的范圍沒(méi)有限制饺窿。
讀取數(shù)組
讀取數(shù)組元素值的一般格式是:${數(shù)組名[下標(biāo)]}歧焦。
例如:valuen=${array[n]}
使用 @ 或 * 符號(hào)可以獲取數(shù)組中的所有元素。
例如:echo ${array[@]}, echo ${array[*]}
數(shù)組長(zhǎng)度
取得數(shù)組元素的個(gè)數(shù)
length=${#array[@]}
或者
length=${#array[*]}
取得數(shù)組單個(gè)元素的長(zhǎng)度
lengthn=${#array[n]}
本Shell規(guī)約規(guī)定
【強(qiáng)制】傳遞數(shù)組使用 "${list[*]}" 形式
【強(qiáng)制】接收數(shù)組使用 array=($*) 形式
示例:
# @return the number of elements in this list
function list_size(){
local array=($1);local size=${#array[*]}
echo "${size}"
}
size=$(list_size "${list[*]}")
assertEquals "${size}" "2"
Shell變量
function f1(){
local var="Hello"
echo "${var} World"
}
注意,變量名和等號(hào)之間不能有空格,這可能和你熟悉的所有編程語(yǔ)言都不一樣肚医。同時(shí),變量名的命名須遵循如下規(guī)則:(參考命名風(fēng)格)
- 局部變量 局部變量在腳本或命令中定義,僅在當(dāng)前shell實(shí)例中有效,其他shell啟動(dòng)的程序不能訪問(wèn)局部變量绢馍。
- 環(huán)境變量 所有的程序,包括shell啟動(dòng)的程序,都能訪問(wèn)環(huán)境變量,有些程序需要環(huán)境變量來(lái)保證其正常運(yùn)行。必要的時(shí)候shell腳本也可以定義環(huán)境變量肠套。
本shell規(guī)約規(guī)定:
【強(qiáng)制】變量取值用 "${}", 使用 {} 包裹,給所有變量加上花括號(hào),防止產(chǎn)生歧義
【強(qiáng)制】變量取值用 "${}", 使用 "" 包裹,防止分詞
【強(qiáng)制】若需要將調(diào)用的函數(shù)的返回結(jié)果賦值給local變量,使用 $(),不推薦使用 ``
【強(qiáng)制】常量使用 readonly 修飾
只讀變量
使用 readonly 命令可以將變量定義為只讀變量,只讀變量的值不能被改變舰涌。
刪除變量
使用 unset 命令可以刪除變量,變量被刪除后不能再次使用。unset 命令不能刪除只讀變量你稚。
關(guān)于注釋
除腳本首行外,所有以 #
開(kāi)頭的語(yǔ)句都將成為注釋瓷耙。
示例:
# 主函數(shù) []<-() <-------函數(shù)注釋這樣寫(xiě)
function main(){
local var="Hello World!!!"
echo "${var}"
}
# info級(jí)別的日志 []<-(msg:String) <-------帶入?yún)⒌暮瘮?shù)注釋
log_info(){
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: [info] $*" >&2
}
# error級(jí)別的日志 []<-(msg:String) <-------帶入?yún)⒌暮瘮?shù)注釋
log_error(){
# todo [error]用紅色顯示 <------函數(shù)內(nèi)注釋
local msg=$1 #將要輸出的日志內(nèi)容 <------變量的注釋緊跟在變量的后面
if [[ x"${msg}" != x"" ]];then
# 注釋 <-------函數(shù)內(nèi)注釋 `#` 與縮進(jìn)格式對(duì)整齊
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]:[error] $*" >&2
fi
}
本Shell規(guī)約規(guī)定
【強(qiáng)制】函數(shù)需有注釋標(biāo)識(shí)該函數(shù)的用途、入?yún)⒆兞康罄怠⒑瘮?shù)的返回值類型
【強(qiáng)制】函數(shù)的注釋 `#` 頂格寫(xiě), 井號(hào)后面緊跟一個(gè)空格,對(duì)于該格式的要求是為了最后生成函數(shù)的幫助文檔是用的(markdown語(yǔ)法),然后是注釋的內(nèi)容,注釋盡量簡(jiǎn)短且在一行,最后跟的是函數(shù)的類型哺徊。
【強(qiáng)制】函數(shù)內(nèi)注釋 `#` 與縮進(jìn)格式對(duì)整齊
【強(qiáng)制】變量的注釋緊跟在變量的后面,不推薦換行寫(xiě)注釋
關(guān)于函數(shù)
函數(shù)定義的形式是
function main(){
#函數(shù)執(zhí)行的操作
#函數(shù)的返回結(jié)果
}
或
main(){
#函數(shù)執(zhí)行的操作
#函數(shù)的返回結(jié)果
}
本Shell規(guī)約規(guī)定
【推薦】使用關(guān)鍵字 `function` 顯示定義的函數(shù)為 public 的函數(shù),可以供 外部腳本以 `sh 腳本 函數(shù) 函數(shù)入?yún) 的形式調(diào)用,可以認(rèn)為成Java當(dāng)中的public的方法
【推薦】未使用關(guān)鍵字 `function` 顯示定義的函數(shù)為 private 的函數(shù), 僅供本腳本內(nèi)部調(diào)用,可以認(rèn)為成Java中的私有方法,注意這種private是人為規(guī)定的,并不是Shell的語(yǔ)法,不推薦以 `sh 腳本 函數(shù) 函數(shù)入?yún) 的形式調(diào)用,注意是不推薦而不是不能。
說(shuō)明:本Shell規(guī)約這樣做的目的就在于使腳本具有一定的封裝性,看到 function
修飾的就知道這個(gè)函數(shù)能被外部調(diào)用, 沒(méi)有被修飾的函數(shù)就僅供內(nèi)部調(diào)用乾闰。你就知道如果你修改的函數(shù)的影響范圍. 如果是 被function 修飾的函數(shù),修改后可能影響到外部調(diào)用他的腳本, 而修改未被function修飾的函數(shù)的時(shí)候,僅僅影響本文件中其他函數(shù)落追。
如 core.sh 腳本內(nèi)容如下是
# 重新設(shè)置DNS地址 []<-()
function setNameServer(){
log_info "setNameServer is ok"
}
# info級(jí)別的日志 []<-(msg:String)
log_info(){
echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[32m [info] \033[0m $*" >&2
}
# error級(jí)別的日志 []<-(msg:String)
log_error(){
# todo [error]用紅色顯示
echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[31m [error] \033[0m $*" >&2
}
則我可以使用 sh core.sh setNameServer
的形式調(diào)用 set_name_server
函數(shù), 但就不推薦使用 sh core.sh log_info "Hello World"
的形式使用 log_info
和 log_error
函數(shù),注意是不推薦不是不能。
函數(shù)調(diào)用
變量涯肩、函數(shù)調(diào)用必須在函數(shù)聲明之后,也就是說(shuō)在用一個(gè)函數(shù)的時(shí)候,這一行命令的前面必須出現(xiàn)了該函數(shù),因?yàn)镾hell的執(zhí)行是從上向下解釋執(zhí)行的轿钠。
同一個(gè)腳本內(nèi)調(diào)用、執(zhí)行調(diào)用另一個(gè)腳本病苗、調(diào)用另一腳本中的命令實(shí)例
#!/usr/bin/env bash
source ./../../BaseShell/Log/BaseLog.sh
function f1(){
echo "I am f1"
}
function main(){
log_info "LINENO:${LINENO} 開(kāi)始執(zhí)行" #調(diào)用 ./../../BaseShell/Log/BaseLog.sh 中的函數(shù),需要先用source BaseLog.sh
f1 #在函數(shù)內(nèi)部調(diào)用當(dāng)前腳本內(nèi)的函數(shù)
log_success "LINENO:${LINENO} 結(jié)束執(zhí)行" #調(diào)用 ./../../BaseShell/Log/BaseLog.sh 中的函數(shù)
}
main #在腳本內(nèi)部調(diào)用當(dāng)前腳本內(nèi)的函數(shù)
bash ChangBaiShanFetcher.sh #執(zhí)行其他腳本
bash ChangBaiShanFetcher.sh main #執(zhí)行其他腳本的main方法,前提是 ChangBaiShanFetcher.sh 腳本 支持按函數(shù)名調(diào)用
函數(shù)參數(shù)
參數(shù) | 說(shuō)明 | |
---|---|---|
$# | 傳遞到腳本的參數(shù)個(gè)數(shù) | |
$* | 以一個(gè)單字符串顯示所有向腳本傳遞的參數(shù)疗垛。如"1 n"的形式輸出所有參數(shù)硫朦。 | |
$$ | 腳本運(yùn)行的當(dāng)前進(jìn)程ID號(hào) | |
$! | 后臺(tái)運(yùn)行的最后一個(gè)進(jìn)程的ID號(hào) | |
$@ | 與@"用「"」括起來(lái)的情況、以"2" … "$n" 的形式輸出所有參數(shù)咬展。 | |
$- | 顯示Shell使用的當(dāng)前選項(xiàng),與set命令功能相同泽裳。 | |
$? | 顯示最后命令的退出狀態(tài)。0表示沒(méi)有錯(cuò)誤,其他任何值表明有錯(cuò)誤破婆。 |
本Shell規(guī)約規(guī)定
【強(qiáng)制】 在函數(shù)內(nèi)部首先使用有意義的變量名接受參數(shù),然后在使用這些變量進(jìn)行操作,禁止直接操作$1,$2等,除非這些變量只用一次
函數(shù)注釋
函數(shù)類型的概念是從函數(shù)編程語(yǔ)言中的概念偷過(guò)來(lái)的,Shell函數(shù)的函數(shù)類型指的是函數(shù)的輸入到函數(shù)的輸入的映射關(guān)系
# 主函數(shù) []<-() <-------函數(shù)注釋這樣寫(xiě)
function main(){
local var="Hello World!!!"
echo ${var}
}
# info級(jí)別的日志 []<-(msg:String) <-------帶入?yún)⒌暮瘮?shù)注釋
log_info(){
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: [info] $*" >&2
}
說(shuō)明:
main函數(shù)的函數(shù)類型是 []<-() , <- 左側(cè)表的是函數(shù)的返回值類型 用[]包裹, 右側(cè)是函數(shù)的參數(shù)類型 用()包裹,多個(gè)參數(shù)用 ',' 分隔,參數(shù)的描述是從 Scala 語(yǔ)言中偷過(guò)來(lái), 先是參數(shù)名稱 然后是參數(shù)類型 中間用:分隔
對(duì)于main函數(shù)的注釋來(lái)說(shuō),
#
頂格寫(xiě),后面緊跟一個(gè)空格,其實(shí)這樣寫(xiě)是遵循的markdown的語(yǔ)法, 后面再跟一個(gè)空格,然后是 []<-(),代表這個(gè)函數(shù)沒(méi)有入?yún)⒁矝](méi)有返回值,這個(gè)函數(shù)的目的就是執(zhí)行這個(gè)這個(gè)函數(shù)中的命令,但我不關(guān)心這個(gè)函數(shù)的返回值涮总。也就是利用函數(shù)的副作用來(lái)完成我們想要的操作。
對(duì)于log_info 也是一樣 不過(guò) 最后的函數(shù)類型是 []<-(msg:String) 代表入?yún)⑹且粋€(gè)string類型的信息,然后也沒(méi)有返回值祷舀。 關(guān)于函數(shù)的返回值,我理解的函數(shù)的返回值有兩種形式,一種是顯示的return一種是隱式的echo
以下是幾種常見(jiàn)的寫(xiě)法
[]<-()
[String]<-(var1:String,var2:String)
[Boolean]<-(var1:String,var2:Int)
[]<-(var1:String)
返回值
Shell 函數(shù)的返回值比較復(fù)雜,獲取函數(shù)的返回值又有多種方式.一般來(lái)說(shuō),一個(gè)函數(shù)內(nèi)的所有標(biāo)準(zhǔn)輸出都作為函數(shù)的返回值瀑梗。注意是標(biāo)準(zhǔn)輸出烹笔。 我們先來(lái)說(shuō),我們執(zhí)行一條命令的時(shí)候, 比如 pwd 正常情況下它輸出的結(jié)果是 當(dāng)前所處的目錄
Last login: Sat Jan 20 17:39:16 on ttys000
chenshang@chenshangMacBook-Pro:~$ pwd
/Users/chenshang
注意我說(shuō)的是正常情況下,那異常情況下呢?輸出的結(jié)果又是什么?輸出的結(jié)果有可能五花八門(mén)!所以,Shell中必然有一種狀態(tài)來(lái)標(biāo)識(shí)一條命令是否執(zhí)行成功,也就是命令執(zhí)行結(jié)果的狀態(tài)。那這種狀態(tài)是怎么標(biāo)識(shí)的,這就引出了Shell中一個(gè)反人類的規(guī)定,就是 0代表真抛丽、成功的含義谤职。非零代表假、失敗的含義亿鲜。
所以 pwd 這條命令如果執(zhí)行成功的話,命令的執(zhí)行結(jié)果狀態(tài)一定是0,然后返回值才是當(dāng)前目錄允蜈。如果這條命令執(zhí)行失敗的話,命令的執(zhí)行結(jié)果狀態(tài)一定不是0,有可能是1 代表命令不存在,然后輸出 not found,也有可能執(zhí)行結(jié)果狀態(tài)是2 代表超時(shí),然后什么也不輸出。(不要以為pwd這種linux內(nèi)帶的命令就一定執(zhí)行成功,有可能你拿到的就是一臺(tái)閹割版的linux呢)
顯示return
return 用來(lái)顯示的返回函數(shù)的返回結(jié)果,例如
# 檢查當(dāng)前系統(tǒng)版本 [Integer]<-()
function checkVersion(){
(log_info "check_version ...") #log_info是我寫(xiě)的工具類中的一個(gè)函數(shù)
local version #這里是先定義變量,在對(duì)變量進(jìn)行賦值,我們往往是直接初始化,而不是像這樣先定義在賦值,這里只是告訴大家可以這么用
version=$(sed -r 's/.* ([0-9]+)\..*/\1/' /etc/redhat-release)
(log_info "centos version is ${version}")
return "${version}"
}
顯示的return結(jié)果,返回值只能是[0-255]的數(shù)值,常常用在狀態(tài)判斷的時(shí)候
這樣這個(gè)函數(shù)的返回值是一個(gè)數(shù)值類型,我在腳本的任何地方調(diào)用 checkVersion 這個(gè)函數(shù)后,使用 $? 獲取返回值
checkVersion
version=$?
echo "${version}"
注意這里不用 version=$(check_version)
這種形式獲取結(jié)果,這樣也是獲取不到結(jié)果的
本Shell規(guī)約規(guī)定:
【推薦】不推薦使用return方式返回,推薦使用echo方式返回結(jié)果
【推薦】返回結(jié)果類型是Boolean類型,也就是說(shuō)函數(shù)的功能是起判斷作用,返回結(jié)果是真或者假的時(shí)候使用顯示 return 返回結(jié)果
例如
# 檢查網(wǎng)絡(luò) [Boolean]<-()
function check_network(){
(log_info "check_network ...")
for((i=1;i<=3;i++));do
local http_code=$(curl -I -m 10 -o /dev/null -s -w %\{http_code\} www.baidu.com)
if [[ ${http_code} -eq 200 ]];then
(log_info "network is ok")
return ${TRUE}
fi
done
(log_error "network is not ok")
return ${FALSE}
}
隱式echo
echo 用來(lái)顯示的返回函數(shù)的返回結(jié)果,例如
# 將json字符串格式化樹(shù)形結(jié)構(gòu) [String]<-(json_string:String)
function json_format(){
local jsonString=$1
echo "${jsonString}"|jq . #jq是Shell中處理json的一個(gè)工具
}
函數(shù)中所有的echo照理都應(yīng)該輸出到控制臺(tái)上 例如
json_format "{\"1\":\"one\"}"
你會(huì)在控制臺(tái)上看到如下輸出
{
"1": "one"
}
但是一旦你用變量接收函數(shù)的返回值,這些本該輸出到控制臺(tái)的結(jié)果就都會(huì)存儲(chǔ)到你定義的變量中 例如
json=$(json_format "{\"1\":\"one\"}")
echo "${json}" #如果沒(méi)有這句,上面的語(yǔ)句執(zhí)行完成后,不會(huì)在控制臺(tái)有任何的輸出
我們把 json_format 改造一下
# 將json字符串格式化樹(shù)形結(jié)構(gòu) [String]<-(json_string:String)
function json_format(){
local jsonString=$1
echo "為格式化之前:${jsonString}" #其實(shí)新添加的這一句只是用來(lái)記錄一行日志的,但是返回結(jié)果會(huì)被外層變量接收
echo "${jsonString}"|jq . #jq是Shell中處理json的一個(gè)工具
}
echo "為格式化之前:(json_format "{"1":"one"}") 變量 json 也會(huì)將這句話作為返回結(jié)果進(jìn)行接收,但這是我不想要看到的狡门。
解決這個(gè)問(wèn)題需要了解重定向相關(guān)
# 將json字符串格式化樹(shù)形結(jié)構(gòu) [String]<-(json_string:String)
function json_format(){
local jsonString=$1
log_trace "為格式化之前:${jsonString}" #其實(shí)新添加的只一句只是用來(lái)記錄一行日志的
log_info "為格式化之前:${jsonString}" #其實(shí)新添加的只一句只是用來(lái)記錄一行日志的
echo "${jsonString}"|jq . #jq是Shell中處理json的一個(gè)工具
}
詳情請(qǐng)看 BaseLog.sh 中對(duì)日志的處理
關(guān)于分支
HEAD_KEYWORD parameters; BODY_BEGIN
BODY_COMMANDS
BODY_END
本Shell規(guī)約規(guī)定
【強(qiáng)制】將HEAD_KEYWORD和初始化命令或者參數(shù)放在第一行陷寝;
【強(qiáng)制】將BODY_BEGIN同樣放在第一行锅很;
【強(qiáng)制】復(fù)合命令中的BODY部分以2個(gè)空格縮進(jìn)其馏;
【強(qiáng)制】BODY_END部分獨(dú)立一行放在最后;
【推薦】parameters部分test表達(dá)式變量取值都用""包裹爆安;
【推薦】parameters部分test表達(dá)式統(tǒng)一使用=等符號(hào), 在明確是數(shù)字的時(shí)候可以使用 -eq等參數(shù)叛复;
- if
if [[ condition ]]; then
echo statements
fi
if [[ condition ]]; then
echo statements
else
echo statements
fi
if [[ condition ]]; then
echo statements
elif [[ condition ]]; then
echo statements
else
echo statements
fi
- while
while [[ condition ]]; do
echo statements
done
while read -r item ;do
echo "${item}"
done < 'file_name'
- until
until [[ condition ]]; do
echo statements
done
- for
for (( i = 0; i < 10; i++ )); do
echo statements
done
for item in ${array}; do
echo "${item}"
done
- case
case word in
pattern )
#statements
;;
*)
#statements
;;
esac
本Shell規(guī)約規(guī)定
【強(qiáng)制】 if\while\until 后面的判斷 使用 雙中括號(hào)`[[]]`
數(shù)學(xué)計(jì)算
注釋 1:shell 的自加、自減操作符在使用上和 c 語(yǔ)言一樣扔仓。-- 或者 ++ 出現(xiàn)在變量前面是前綴形式,先運(yùn)算后賦值褐奥;-- 或者 ++ 出現(xiàn)在變量后面是后綴形式,先賦值后運(yùn)算。 本Shell規(guī)約規(guī)定
【推薦】明確知道變量是整數(shù),計(jì)算使用$(())包裹計(jì)算,(())內(nèi)對(duì)變量的操作不用$取值
正確 a=$((1+1))
反例 a=$(($a++))
【推薦】復(fù)雜計(jì)算使用bc計(jì)算器,前提是得安裝bc計(jì)算器命令
關(guān)于進(jìn)程間通信
管道
前一個(gè)命令的標(biāo)磚輸出作為下一個(gè)命令的標(biāo)準(zhǔn)輸入ps -ef|grep java
有名管道
命名管道也被稱為FIFO文件,是一種特殊的文件翘簇。由于linux所有的事物都可以被視為文件,所以對(duì)命名管道的使用也就變得與文件操作非常統(tǒng)一撬码。
若對(duì)管道文件直接進(jìn)行操作,可實(shí)現(xiàn)阻塞讀寫(xiě),如果沒(méi)有讀則寫(xiě)阻塞,如果沒(méi)有寫(xiě)則讀阻塞 若使用文件描述符與有名管道文件進(jìn)行關(guān)聯(lián),可實(shí)現(xiàn)非阻塞讀寫(xiě),如果沒(méi)有讀,則寫(xiě)不阻塞,如果沒(méi)有內(nèi)容則讀阻塞 使用read 可以設(shè)置讀超時(shí),并且讀取管道中的值
基于以上原理可以實(shí)現(xiàn)Concurrent包下的函數(shù)庫(kù)
debug
- sh -x
- 在合適位置 set -x
- bashdb 單步調(diào)試
- 單獨(dú)測(cè)試某一個(gè)函數(shù)
單元測(cè)試
以test-開(kāi)頭的都會(huì)進(jìn)行測(cè)試
#!/usr/bin/env bash
# shellcheck disable=SC1091
#################引入需要測(cè)試的腳本################
source ./../../BaseShell/Collection/BaseHashMap.sh
###################下面寫(xiě)單元測(cè)試#################
###################上面寫(xiě)單元測(cè)試#################
source ./../../BaseShell/Utils/BaseTestUtil.sh
命名風(fēng)格
本Shell規(guī)約規(guī)定
【強(qiáng)制】腳本中變量、函數(shù)版保、文件的命名均不能以數(shù)字呜笑、下劃線、$開(kāi)頭,也不能以下劃線或者$結(jié)尾
反例: _name / __name / $name / name_ / name$ / 5name
說(shuō)明: $作為Shell語(yǔ)言的取值符號(hào),其他命名約束參考Java規(guī)約彻犁。
【強(qiáng)制】代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式叫胁。
正例:alibaba / taobao / youku / hangzhou 等國(guó)際通用的名稱,可視同英文。
說(shuō)明:正確的英文拼寫(xiě)和語(yǔ)法可以讓閱讀者易于理解,避免歧義汞幢。注意,即使純拼音命名方式 也要避免采用驼鹅。
【強(qiáng)制】參數(shù)名、局部變量都統(tǒng)一使用 lowerCamelCase 風(fēng)格,必須遵從 駝峰形式森篷。
正例: localValue / errMsg / userName
【推薦】使用下劃線分割函數(shù)命名,都統(tǒng)一使用 lowerCamelCase 風(fēng)格,必須遵從 駝峰形式输钩。
正例:getUserName() / log_info() / map_add()
說(shuō)明: 純用下劃線會(huì)使得命名很長(zhǎng),純用駝峰又無(wú)法將函數(shù)聚類
get_user_english_name() vs getUserEnglishName()
log_info() vs logInfo()
map_add() vs mapAdd()
`log_info "xxx"` 在調(diào)用的時(shí)候類比于Java 的`log.info("xxx")`
【強(qiáng)制】文件名 UpperCamelCase 風(fēng)格,首字母大寫(xiě)的駝峰形式
正例:BaseLog.sh / BaseString.sh
說(shuō)明:類比Java的類,我們把相同功能的函數(shù)抽象到一個(gè)腳本文件中,留待后續(xù)其他腳本引用。隨著我們寫(xiě)的腳本日漸增多
我們很有必要分門(mén)別類的將它們聚集到一個(gè)文本文件中,一是方便后續(xù)查閱,而是方便后續(xù)調(diào)用
【強(qiáng)制】常量命名全部大寫(xiě),單詞間用下劃線隔開(kāi),力求語(yǔ)義表達(dá)完整清楚,不要嫌名字長(zhǎng)仲智。
正例:MAX_STOCK_COUNT 反例:MAX_COUNT
【推薦】文件夾名字大寫(xiě)
【強(qiáng)制】文件名以 .sh 結(jié)尾
說(shuō)明:雖然不用.sh結(jié)尾或者以任何其他新式結(jié)尾都可以運(yùn)行,但是以 .sh結(jié)尾可以一眼看出就是腳本文件
本Shell規(guī)約規(guī)定
【強(qiáng)制】使用兩個(gè)空格進(jìn)行縮進(jìn),不適用tab縮進(jìn)
【推薦】不在一行的時(shí)候使用 `\` 進(jìn)行換行,使用 `\` 換行的原則是整齊美觀
例子:
#!/usr/bin/env bash
# 腳本使用幫助文檔 []<-()
manual(){
cat "$0"|grep -v "less \"\$0\"" \
|grep -B1 "function " \
|grep -v "\\--" \
|sed "s/function //g" \
|sed "s/(){//g" \
|sed "s/#//g" \
|sed 'N;s/\n/ /' \
|column -t \
|awk '{print $1,$3,$2}' \
|column -t
}
function search_user_info(){
local result=$(httpclient_get --cookie "${cookie}" \
"${url}/userName=${user_name}")
}
()张足、(())、[]坎藐、[[]]为牍、{}
使用方法
( )
命令組哼绑。
括號(hào)中的命令將會(huì)新開(kāi)一個(gè)子shell順序執(zhí)行,所以括號(hào)中的變量不能夠被腳本余下的部分使用。括號(hào)中多個(gè)命令之間用分號(hào)';'隔開(kāi),最后一個(gè)命令可以沒(méi)有分號(hào),各命令和括號(hào)之間不必有空格碉咆。
命令替換抖韩。
等同于 command
,shell掃描一遍命令行,發(fā)現(xiàn)了(command)中的command執(zhí)行一次,得到其標(biāo)準(zhǔn)輸出,再將此輸出放到原來(lái)命令。
用于初始化數(shù)組疫铜。
如:array=(a b c d e f)
(( ))
這種擴(kuò)展計(jì)算是整數(shù)型的計(jì)算,不支持浮點(diǎn)型茂浮。((exp))結(jié)構(gòu)擴(kuò)展并計(jì)算一個(gè)算術(shù)表達(dá)式的值,如果表達(dá)式的結(jié)果為0,那么返回的退出狀態(tài)碼為1,或者是"false",而一個(gè)非零值的表達(dá)式所返回的退出狀態(tài)碼將為0,或者是"true"。若是邏輯判斷,表達(dá)式exp為真則為1,假則為0壳咕。
只要括號(hào)中的運(yùn)算符席揽、表達(dá)式符合C語(yǔ)言運(yùn)算規(guī)則,都可用在((16#5f)) 結(jié)果為95 (16進(jìn)制轉(zhuǎn)十進(jìn)制) 單純用 (( )) 也可重定義變量值,比如 a=5; ((a++))可將 符號(hào)前綴竟稳。括號(hào)內(nèi)支持多個(gè)表達(dá)式用逗號(hào)分開(kāi)属桦。只要括號(hào)中的表達(dá)式符合C語(yǔ)言運(yùn)算規(guī)則,比如可以直接使用for((i=0;i<5;i++)), 如果不使用雙括號(hào), 則為for i in
seq 0 4
或者for i in {0..4}。再如可以直接使用if ((i -lt 5 ]他爸。
[ ]
- bash 的內(nèi)部命令,[和test是等同的聂宾。如果我們不用絕對(duì)路徑指明,通常我們用的都是bash自帶的命令。if/test結(jié)構(gòu)中的左中括號(hào)是調(diào)用test的命令標(biāo)識(shí),右中括號(hào)是關(guān)閉條件判斷的诊笤。這個(gè)命令把它的參數(shù)作為比較表達(dá)式或者作為文件測(cè)試,并且根據(jù)比較的結(jié)果來(lái)返回一個(gè)退出狀態(tài)碼系谐。if/test結(jié)構(gòu)中并不是必須右中括號(hào),但是新版的Bash中要求必須這樣。
- test和[]中可用的比較運(yùn)算符只有==和!=,兩者都是用于字符串比較的,不可用于整數(shù)比較,整數(shù)比較只能使用-eq,-gt這種形式讨跟。無(wú)論是字符串比較還是整數(shù)比較都不支持大于號(hào)小于號(hào)纪他。如果實(shí)在想用,對(duì)于字符串比較可以使用轉(zhuǎn)義形式,如果比較"ab"和"bc":[ ab < bc ],結(jié)果為真,也就是返回狀態(tài)為0。[ ]中的邏輯與和邏輯或使用-a 和-o 表示许赃。
- 字符范圍止喷。用作正則表達(dá)式的一部分,描述一個(gè)匹配的字符范圍。作為test用途的中括號(hào)內(nèi)不能使用正則混聊。
- 在一個(gè)array 結(jié)構(gòu)的上下文中,中括號(hào)用來(lái)引用數(shù)組中每個(gè)元素的編號(hào)弹谁。
[[ ]]
- [[是 bash 程序語(yǔ)言的關(guān)鍵字。并不是一個(gè)命令,[[ ]] 結(jié)構(gòu)比[ ]結(jié)構(gòu)更加通用句喜。在[[和]]之間所有的字符都不會(huì)發(fā)生文件名擴(kuò)展或者單詞分割,但是會(huì)發(fā)生參數(shù)擴(kuò)展和命令替換预愤。
- 支持字符串的模式匹配,使用=~操作符時(shí)甚至支持shell的正則表達(dá)式。字符串比較時(shí)可以把右邊的作為一個(gè)模式,而不僅僅是一個(gè)字符串,比如[[ hello == hell? ]],結(jié)果為真咳胃。[[ ]] 中匹配字符串或通配符,不需要引號(hào)植康。
- 使用[[ ... ]]條件判斷結(jié)構(gòu),而不是[ ... ],能夠防止腳本中的許多邏輯錯(cuò)誤。比如,&&展懈、||销睁、<和> 操作符能夠正常存在于[[ ]]條件判斷結(jié)構(gòu)中,但是如果出現(xiàn)在[ ]結(jié)構(gòu)中的話,會(huì)報(bào)錯(cuò)供璧。比如可以直接使用if [[ a != 2 ]], 如果不適用雙括號(hào), 則為if [ a != 2 ]或者if [ a != 2 ]。
- bash把雙中括號(hào)中的表達(dá)式看作一個(gè)單獨(dú)的元素,并返回一個(gè)退出狀態(tài)碼冻记。
{ }
- 對(duì)大括號(hào){}中的以逗號(hào)分割的文件列表進(jìn)行拓展睡毒。如 touch {a,b}.txt 結(jié)果為a.txt b.txt。
- 對(duì)大括號(hào){}中以點(diǎn)點(diǎn)(..)分割的順序文件列表起拓展作用,如:touch {a..d}.txt 結(jié)果為a.txt b.txt c.txt d.txt冗栗。
- 代碼塊,又被稱為內(nèi)部組,這個(gè)結(jié)構(gòu)事實(shí)上創(chuàng)建了一個(gè)匿名函數(shù) 演顾。 與()中的命令不同,{}內(nèi)的命令不會(huì)新開(kāi)一個(gè)子shell運(yùn)行,即腳本余下部分仍可使用括號(hào)內(nèi)變量。括號(hào)內(nèi)的命令間用分號(hào)隔開(kāi),最后一個(gè)也必須有分號(hào)隅居。{}的第一個(gè)命令和左括號(hào)之間必須要有一個(gè)空格钠至。
特殊的替換結(jié)構(gòu):
-
${var:-string}
和${var:=string}
:若變量var為空,則用在命令行中用string來(lái)替換${var:-string}
,否則變量var不為空時(shí),則用變量var的值來(lái)替換${var:-string}
;對(duì)于${var:=string}
的替換規(guī)則和${var:-string}
是一樣的,所不同之處是${var:=string}
若var為空時(shí),用string替換${var:=string}
的同時(shí),把string賦給變量var,${var:=string}
很常用的一種用法是,判斷某個(gè)變量是否賦值,沒(méi)有的話則給它賦上一個(gè)默認(rèn)值胎源。 -
${var:+string}
的替換規(guī)則和上面的相反,即只有當(dāng)var不是空的時(shí)候才替換成string,若var為空時(shí)則不替換或者說(shuō)是替換成變量 var的值,即空值棉钧。 -
${var:?string}
替換規(guī)則為:若變量var不為空,則用變量var的值來(lái)替換${var:?string};若變量var為空,則把string輸出到標(biāo)準(zhǔn)錯(cuò)誤中,并從腳本中退出乒融。我們可利用此特性來(lái)檢查是否設(shè)置了變量的值掰盘。 - `${variable%pattern}這種模式,shell在variable中查找,看它是否以給的模式pattern結(jié)尾,如果是,就從命令行把variable中的內(nèi)容去掉右邊最短的匹配模式
-
${variable%%pattern}
這種模式,shell在variable中查找,看它是否以給的模式pattern結(jié)尾,如果是,就從命令行把variable中的內(nèi)容去掉右邊最長(zhǎng)的匹配模式 -
${variable#pattern}
這種模式,shell在variable中查找,看它是否以給的模式pattern開(kāi)始,如果是,就從命令行把variable中的內(nèi)容去掉左邊最短的匹配模式 -
${variable##pattern}
這種模式,shell在variable中查找,看它是否以給的模式pattern結(jié)尾,如果是,就從命令行把variable中的內(nèi)容去掉右邊最長(zhǎng)的匹配模式 這四種模式中都不會(huì)改變variable的值,其中,只有在pattern中使用了匹配符號(hào)時(shí),%和%%,#和##才有區(qū)別摄悯。結(jié)構(gòu)中的pattern支持通配符,表示零個(gè)或多個(gè)任意字符,?表示僅與一個(gè)任意字符匹配,[...]表示匹配中括號(hào)里面的字符,[!...]表示不匹配中括號(hào)里面的字符赞季。**
字符串提取和替換
-
${var:num}
,shell在var中提取第num個(gè)字符到末尾的所有字符。若num為正數(shù),從左邊0處開(kāi)始奢驯;若num為負(fù)數(shù),從右邊開(kāi)始提取字串,但必須使用在冒號(hào)后面加空格或一個(gè)數(shù)字或整個(gè)num加上括號(hào),如{var:1-3}或
${var:(-2)}`。 -
${var:num1:num2}
,num1是位置,num2是長(zhǎng)度瘪阁。表示從num1個(gè)位置開(kāi)始提取長(zhǎng)度為$num2的子串撒遣。不能為負(fù)數(shù)。 -
${var/pattern1/pattern2}
表示將var字符串的第一個(gè)匹配的pattern1替換為另一個(gè)pattern2管跺。 -
${var//pattern1/pattern2}
表示將var字符串中的所有能匹配的pattern1替換為另一個(gè)pattern2义黎。