前言 由于工作需要,,最近重新開始拾掇shell腳本,。雖然絕大部分命令自己平時也經(jīng)常使用,但是在寫成腳本的時候總覺得寫的很難看,。而且當(dāng)我在看其他人寫的腳本的時候,,總覺得難以閱讀,。畢竟shell腳本這個東西不算是正經(jīng)的編程語言,他更像是一個工具,,用來雜糅不同的程序供我們調(diào)用,。因此很多人在寫的時候也是想到哪里寫到哪里,基本上都像是一段超長的main函數(shù),,不忍直視,。同時,由于歷史原因,,shell有很多不同的版本,,而且也有很多有相同功能的命令需要我們進(jìn)行取舍,以至于代碼的規(guī)范很難統(tǒng)一,。 代碼風(fēng)格規(guī)范開頭有“蛇棒”所謂shebang其實就是在很多腳本的第一行出現(xiàn)的以”#!”開頭的注釋,,他指明了當(dāng)我們沒有指定解釋器的時候默認(rèn)的解釋器,一般可能是下面這樣: #!/bin/bash 當(dāng)然,,解釋器有很多種,,除了bash之外,我們可以用下面的命令查看本機支持的解釋器: $ cat /etc/shells#/etc/shells: valid login shells/bin/sh/bin/dash/bin/bash/bin/rbash/usr/bin/screen 當(dāng)我們直接使用 #!/usr/bin/env bash 這種方式是我們推薦的使用方式,。 代碼有注釋 注釋,,顯然是一個常識,不過這里還是要再強調(diào)一下,,這個在shell腳本里尤為重要,。因為很多單行的shell命令不是那么淺顯易懂,沒有注釋的話在維護(hù)起來會讓人尤其的頭大,。
參數(shù)要規(guī)范 這一點很重要,當(dāng)我們的腳本需要接受參數(shù)的時候,,我們一定要先判斷參數(shù)是否合乎規(guī)范,,并給出合適的回顯,方便使用者了解參數(shù)的使用,。 if [[ $# != 2 ]];then echo 'Parameter incorrect.' exit 1fi 變量和魔數(shù) 一般情況下我們會將一些重要的環(huán)境變量定義在開頭,,確保這些變量的存在,。 source /etc/profileexport PATH=”/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/apps/bin/” 這種定義方式有一個很常見的用途,最典型的應(yīng)用就是,當(dāng)我們本地安裝了很多java版本時,,我們可能需要指定一個java來用,。那么這時我們就會在腳本開頭重新定義 同時,,一段好的代碼通常是不會有很多硬編碼在代碼里的“魔數(shù)”的,。如果一定要有,通常是用一個變量的形式定義在開頭,,然后調(diào)用的時候直接調(diào)用這個變量,,這樣方便日后的修改。 縮進(jìn)有規(guī)矩 對于shell腳本,,縮進(jìn)是個大問題,。因為很多需要縮進(jìn)的地方(比如if,for語句)都不長,所有很多人都懶得去縮進(jìn),,而且很多人不習(xí)慣用函數(shù),,導(dǎo)致縮進(jìn)功能被弱化。
命名有標(biāo)準(zhǔn)所謂命名規(guī)范,基本包含下面這幾點:
編碼要統(tǒng)一在寫腳本的時候盡量使用UTF-8編碼,,能夠支持中文等一些奇奇怪怪的字符。不過雖然能寫中文,,但是在寫注釋以及打log的時候還是盡量英文,,畢竟很多機器還是沒有直接支持中文的,打出來可能會有亂碼。 權(quán)限記得加這一點雖然很小,,但是我個人卻經(jīng)常忘記,,不加執(zhí)行權(quán)限會導(dǎo)致無法直接執(zhí)行,有點討厭,。,。。 日志和回顯 日志的重要性不必多說,,能夠方便我們回頭糾錯,,在大型的項目里是非常重要的。 密碼要移除 不要把密碼硬編碼在腳本里,不要把密碼硬編碼在腳本里,,不要把密碼硬編碼在腳本里,。 太長要分行在調(diào)用某些程序的時候,,參數(shù)可能會很長,,這時候為了保證較好的閱讀體驗,我們可以用反斜杠來分行: ./configure \–prefix=/usr \–sbin-path=/usr/sbin/nginx \–conf-path=/etc/nginx/nginx.conf \ 注意在反斜杠前有個空格,。 編碼細(xì)節(jié)規(guī)范代碼有效率 在使用命令的時候要了解命令的具體做法,,尤其當(dāng)數(shù)據(jù)處理量大的時候,要時刻考慮該命令是否會影響效率,。 sed -n '1p' filesed -n '1p;1q' file 他們的作用一樣,,都是獲取文件的第一行。但是第一條命令會讀取整個文件,,而第二條命令只讀取第一行,。當(dāng)文件很大的時候,僅僅是這樣一條命令不一樣就會造成巨大的效率差異,。 勤用雙引號 幾乎所有的大佬都推薦在使用”$”來獲取變量的時候最好加上雙引號。 #!/bin/sh#已知當(dāng)前文件夾有一個a.sh的文件var='*.sh'echo $varecho '$var' 他的運行結(jié)果如下: a.sh*.sh 為啥會這樣呢?其實可以解釋為它執(zhí)行了下面的命令: echo *.shecho '*.sh' 在很多情況下,,在將變量作為參數(shù)的時候,,一定要注意上面這一點,仔細(xì)體會其中的差異,。上面只是一個非常小的例子,,實際應(yīng)用的時候由于這個細(xì)節(jié)導(dǎo)致的問題實在是太多了。,。,。 巧用main函數(shù) 我們知道,像java,,C這樣的編譯型語言都會有一個函數(shù)入口,,這種結(jié)構(gòu)使得代碼可讀性很強,我們知道哪些直接執(zhí)行,,那些是函數(shù),。但是腳本不一樣,腳本屬于解釋性語言,,從第一行直接執(zhí)行到最后一行,,如果在這當(dāng)中命令與函數(shù)糅雜在一起,,那就非常難讀了,。 #!/usr/bin/env pythondef func1(): passdef func2(): passif __name__=='__main__': func1() func2() 他用一個很巧妙的方法實現(xiàn)了我們習(xí)慣的main函數(shù),,使得代碼可讀性更強,。 #!/usr/bin/env bashfunc1(){ #do sth}func2(){ #do sth}main(){ func1 func2}main '$@' 我們可以采用這種寫法,,同樣實現(xiàn)類似的main函數(shù),,使得腳本的結(jié)構(gòu)化程度更好。 考慮作用域shell中默認(rèn)的變量作用域都是全局的,,比如下面的腳本: #!/usr/bin/env bashvar=1func(){ var=2}funcecho $var 他的輸出結(jié)果就是2而不是1,,這樣顯然不符合我們的編碼習(xí)慣,很容易造成一些問題,。 巧用heredocs 所謂heredocs,也可以算是一種多行輸入的方法,,即在”<<”后定一個標(biāo)識符,,接著我們可以輸入多行內(nèi)容,直到再次遇到標(biāo)識符為止,。 cat>>/etc/rsyncd.conf << EOFlog file = /usr/local/logs/rsyncd.logtransfer logging = yeslog format = %t %a %m %f %bsyslog facility = local3EOF 學(xué)會查路徑 很多情況下,我們會先獲取當(dāng)前腳本的路徑,,然后一這個路徑為基準(zhǔn),,去找其他的路徑。通常我們是直接用 script_dir=$(cd $(dirname $0) && pwd)script_dir=$(dirname $(readlink -f $0 )) 應(yīng)當(dāng)先cd進(jìn)當(dāng)前腳本的目錄然后再pwd,,或者直接讀取當(dāng)前腳本的所在路徑,。 代碼要簡短 這里的簡短不單單是指代碼長度,而是只用到的命令數(shù),。原則上我們應(yīng)當(dāng)做到,,能一條命令解決的問題絕不用兩條命令解決。這不僅牽涉到代碼的可讀性,,而且也關(guān)乎代碼的執(zhí)行效率,。 cat /etc/passwd | grep rootgrep root /etc/passwd cat命令最為人不齒的用法就是這樣,用的沒有任何意義,,明明一條命令可以解決,,他非得加根管道。,。,。 使用新寫法這里的新寫法不是指有多厲害,而是指我們可能更希望使用較新引入的一些語法,,更多是偏向代碼風(fēng)格的,,比如
事實上,,這些新寫法很多功能都比舊的寫法要強大,用的時候就知道了,。 其他小tip考慮到還有很多零碎的點,,就不一一展開了,,這里簡單提一提。
靜態(tài)檢查工具shellcheck概述 為了從制度上保證腳本的質(zhì)量,,我們最簡單的想法大概就是搞一個靜態(tài)檢查工具,,通過引入工具來彌補開發(fā)者可能存在的知識盲點。 安裝這個工具的對不同平臺的支持力度都很大,,他至少支持了Debian,Arch,Gentoo,EPEL,Fedora,OS X,openSUSE等等各種的平臺的主流包管理工具,。安裝方便。具體可以參照安裝文檔 集成既然是靜態(tài)檢查工具,,就一定可以集成在CI框架里,,shellcheck可以非常方便的集成在Travis CI中,供以shell腳本為主語言的項目進(jìn)行靜態(tài)檢查,。 樣例在文檔的Gallery of bad code里,,也提供了非常詳細(xì)的“壞代碼”的標(biāo)準(zhǔn),具有非常不錯的參考價值,,可以在閑下來的時候當(dāng)成”Java Puzzlers“之類的書來讀讀還是很愜意的。 本質(zhì)不過,,其實我覺得這個項目最最精華的部分都不是上面的功能,,而是他提供了一個非常非常強大的wiki。在這個wiki里,,我們可以找到這個工具所有判斷的依據(jù),。在這里,,每一個檢測到的問題都可以在wiki里找到對應(yīng)的問題單號,他不僅告訴我們”這樣寫不好”,,而且告訴我們”為什么這樣寫不好”,,”我們應(yīng)當(dāng)怎么寫才好”,非常適合刨根問底黨進(jìn)一步研究,。 參考資料 |
|