久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

編寫Shell腳本的最佳實踐

 蘭亭文藝 2018-07-14

  前言

  由于工作需要,,最近重新開始拾掇shell腳本,。雖然絕大部分命令自己平時也經(jīng)常使用,但是在寫成腳本的時候總覺得寫的很難看,。而且當(dāng)我在看其他人寫的腳本的時候,,總覺得難以閱讀,。畢竟shell腳本這個東西不算是正經(jīng)的編程語言,他更像是一個工具,,用來雜糅不同的程序供我們調(diào)用,。因此很多人在寫的時候也是想到哪里寫到哪里,基本上都像是一段超長的main函數(shù),,不忍直視,。同時,由于歷史原因,,shell有很多不同的版本,,而且也有很多有相同功能的命令需要我們進(jìn)行取舍,以至于代碼的規(guī)范很難統(tǒng)一,。
  考慮到上面的這些原因,,我查閱了一些相關(guān)的文檔,發(fā)現(xiàn)這些問題其實很多人都考慮過,,而且也形成了一些不錯的文章,,但是還是有點零散,。因此我就在這里把這些文章稍微整理了一下,作為以后我自己寫腳本的技術(shù)規(guī)范,。

  代碼風(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)我們直接使用./a.sh來執(zhí)行這個腳本的時候,,如果沒有shebang,,那么它就會默認(rèn)用$SHELL指定的解釋器,否則就會用shebang指定的解釋器,。
  不過,,上面這種寫法可能不太具備適應(yīng)性,一般我們會用下面的方式來指定:

#!/usr/bin/env bash

  這種方式是我們推薦的使用方式,。

  代碼有注釋

  注釋,,顯然是一個常識,不過這里還是要再強調(diào)一下,,這個在shell腳本里尤為重要,。因為很多單行的shell命令不是那么淺顯易懂,沒有注釋的話在維護(hù)起來會讓人尤其的頭大,。
  注釋的意義不僅在于解釋用途,,而在于告訴我們注意事項,就像是一個README,。
  具體的來說,,對于shell腳本,注釋一般包括下面幾個部分:

  1. shebang
  2. 腳本的參數(shù)
  3. 腳本的用途
  4. 腳本的注意事項
  5. 腳本的寫作時間,,作者,,版權(quán)等
  6. 各個函數(shù)前的說明注釋
  7. 一些較復(fù)雜的單行命令注釋

  參數(shù)要規(guī)范

  這一點很重要,當(dāng)我們的腳本需要接受參數(shù)的時候,,我們一定要先判斷參數(shù)是否合乎規(guī)范,,并給出合適的回顯,方便使用者了解參數(shù)的使用,。
  最少,,最少,我們至少得判斷下參數(shù)的個數(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來用,。那么這時我們就會在腳本開頭重新定義JAVA_HOME以及PATH變量來進(jìn)行控制。

  同時,,一段好的代碼通常是不會有很多硬編碼在代碼里的“魔數(shù)”的,。如果一定要有,通常是用一個變量的形式定義在開頭,,然后調(diào)用的時候直接調(diào)用這個變量,,這樣方便日后的修改。

  縮進(jìn)有規(guī)矩

  對于shell腳本,,縮進(jìn)是個大問題,。因為很多需要縮進(jìn)的地方(比如if,for語句)都不長,所有很多人都懶得去縮進(jìn),,而且很多人不習(xí)慣用函數(shù),,導(dǎo)致縮進(jìn)功能被弱化。
  其實正確的縮進(jìn)是很重要的,,尤其是在寫函數(shù)的時候,,否則我們在閱讀的時候很容易把函數(shù)體跟直接執(zhí)行的命令搞混,。
  常見的縮進(jìn)方法主要有”soft tab”和”hard tab”兩種,。

  • 所謂soft tab就是使用n個空格進(jìn)行縮進(jìn)(n通常是2或4)
  • 所謂hard tab當(dāng)然就是指真實的”\t”字符
    這里不去撕哪種方式最好,只能說各有各的優(yōu)劣,。反正我習(xí)慣用hard tab,。
    對于if和for語句之類的,我們最好不要把then,,do這些關(guān)鍵字單獨寫一行,,這樣看上去比較丑。,。,。

  命名有標(biāo)準(zhǔn)

  所謂命名規(guī)范,基本包含下面這幾點:

  1. 文件名規(guī)范,,以.sh結(jié)尾,,方便識別
  2. 變量名字要有含義,不要拼錯
  3. 統(tǒng)一命名風(fēng)格,,寫shell一般用小寫字母加下劃線

  編碼要統(tǒng)一

  在寫腳本的時候盡量使用UTF-8編碼,,能夠支持中文等一些奇奇怪怪的字符。不過雖然能寫中文,,但是在寫注釋以及打log的時候還是盡量英文,,畢竟很多機器還是沒有直接支持中文的,打出來可能會有亂碼。

  權(quán)限記得加

  這一點雖然很小,,但是我個人卻經(jīng)常忘記,,不加執(zhí)行權(quán)限會導(dǎo)致無法直接執(zhí)行,有點討厭,。,。。

  日志和回顯

  日志的重要性不必多說,,能夠方便我們回頭糾錯,,在大型的項目里是非常重要的。
  如果這個腳本是供用戶直接在命令行使用的,,那么我們最好還要能夠在執(zhí)行時實時回顯執(zhí)行過程,,方便用戶掌控。
  有時候為了提高用戶體驗,,我們會在回顯中添加一些特效,,比如顏色啊,閃爍啊之類的,,具體可以參考 ANSI/VT100 Control sequences 這篇文章的介紹,。

  密碼要移除

  不要把密碼硬編碼在腳本里,不要把密碼硬編碼在腳本里,,不要把密碼硬編碼在腳本里,。
  重要的事情說三遍,尤其是當(dāng)腳本托管在類似Github這類平臺中時,。,。。

  太長要分行

  在調(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命令:

sed -n '1p' filesed -n '1p;1q' file

  他們的作用一樣,,都是獲取文件的第一行。但是第一條命令會讀取整個文件,,而第二條命令只讀取第一行,。當(dāng)文件很大的時候,僅僅是這樣一條命令不一樣就會造成巨大的效率差異,。
  當(dāng)然,,這里只是為了舉一個例子,這個例子真正正確的用法應(yīng)該是使用head -n1 file命令。,。,。

  勤用雙引號

  幾乎所有的大佬都推薦在使用”$”來獲取變量的時候最好加上雙引號。
  不加上雙引號在很多情況下都會造成很大的麻煩,,為什么呢,?舉一個例子:

#!/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ù)糅雜在一起,,那就非常難讀了,。
  用python的朋友都知道,一個合乎標(biāo)準(zhǔn)的python腳本大體上至少是這樣的:

#!/usr/bin/env pythondef func1(): passdef func2(): passif __name__=='__main__': func1() func2()

  他用一個很巧妙的方法實現(xiàn)了我們習(xí)慣的main函數(shù),,使得代碼可讀性更強,。
  在shell中,我們也有類似的小技巧:

#!/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í)慣,很容易造成一些問題,。
  因此,,相比直接使用全局變量,我們最好使用local readonly這類的命令,其次我們可以使用declare來聲明變量,。這些方式都比使用全局方式定義要好,。

  巧用heredocs

  所謂heredocs,也可以算是一種多行輸入的方法,,即在”<<”后定一個標(biāo)識符,,接著我們可以輸入多行內(nèi)容,直到再次遇到標(biāo)識符為止,。
  使用heredocs,,我們可以非常方便的生成一些模板文件:

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),,去找其他的路徑。通常我們是直接用pwd以期獲得腳本的路徑,。
  不過其實這樣是不嚴(yán)謹(jǐn)?shù)模?code>pwd獲得的是當(dāng)前shell的執(zhí)行路徑,,而不是當(dāng)前腳本的執(zhí)行路徑。
  正確的做法應(yīng)該是下面這兩種:

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í)行效率,。
  最最經(jīng)典的例子如下:

cat /etc/passwd | grep rootgrep root /etc/passwd

  cat命令最為人不齒的用法就是這樣,用的沒有任何意義,,明明一條命令可以解決,,他非得加根管道。,。,。

  使用新寫法

  這里的新寫法不是指有多厲害,而是指我們可能更希望使用較新引入的一些語法,,更多是偏向代碼風(fēng)格的,,比如

  1. 盡量使用func(){}來定義函數(shù),而不是func{}
  2. 盡量使用[[]]來代替[]
  3. 盡量使用$()將命令的結(jié)果賦給變量,,而不是反引號
  4. 在復(fù)雜的場景下盡量使用printf代替echo進(jìn)行回顯

  事實上,,這些新寫法很多功能都比舊的寫法要強大,用的時候就知道了,。

  其他小tip

  考慮到還有很多零碎的點,,就不一一展開了,,這里簡單提一提。

  • 路徑盡量保持絕對路徑,,絕多路徑不容易出錯,,如果非要用相對路徑,最好用./修飾
  • 優(yōu)先使用bash的變量替換代替awk sed,,這樣更加簡短
  • 簡單的if盡量使用&& ||,,寫成單行。比如[[ x > 2]] && echo x
  • 當(dāng)export變量時,,盡量加上子腳本的namespace,,保證變量不沖突
  • 會使用trap捕獲信號,并在接受到終止信號時執(zhí)行一些收尾工作
  • 使用mktemp生成臨時文件或文件夾
  • 利用/dev/null過濾不友好的輸出信息
  • 會利用命令的返回值判斷命令的執(zhí)行情況
  • 使用文件前要判斷文件是否存在,,否則做好異常處理
  • 不要處理ls后的數(shù)據(jù)(比如ls -l | awk '{ print $8 }'),,ls的結(jié)果非常不確定,并且平臺有關(guān)
  • 讀取文件時不要使用for loop而要使用while read

  靜態(tài)檢查工具shellcheck

  概述

  為了從制度上保證腳本的質(zhì)量,,我們最簡單的想法大概就是搞一個靜態(tài)檢查工具,,通過引入工具來彌補開發(fā)者可能存在的知識盲點。
  市面上對于shell的靜態(tài)檢查工具還真不多,,找來找去就找到一個叫shellcheck的工具,,開源在github上,有8K多的star,,看上去還是十分靠譜的,。我們可以去他的主頁了解具體的安裝和使用信息。

  安裝

  這個工具的對不同平臺的支持力度都很大,,他至少支持了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)一步研究,。

  參考資料

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多