日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
編寫B(tài)ash腳本的8個建議

在我最開始管理Linux和Unix服務(wù)器時,經(jīng)常遇到其他管理員編寫的一大堆臨時腳本。時常會因為其中某個腳本突然停止工作而進(jìn)行故障排查。有時這些腳本編寫得規(guī)范好理解,其他時候則是雜亂且令人困惑。

雖然排查編寫糟糕的腳本很麻煩,但我從中吸取到了教訓(xùn)。即使你認(rèn)為該腳本只會在今天使用,最好也抱著兩年后還將有人去排查的態(tài)度編寫腳本。因為總會有人查看,甚至很可能是你自己。

在本篇文章中,我想介紹一些優(yōu)化腳本的建議,不是為了方便你編寫腳本,而是方便想要弄清腳本為何不工作的人。

以釋伴shebang行開頭

Shell腳本編寫的第一條規(guī)則是以釋伴(shebang)行開頭。雖然聽起來很好笑,但釋伴shebang行卻很重要,它告訴系統(tǒng)使用哪種二進(jìn)制作為腳本的解釋器。沒有釋伴shebang行,系統(tǒng)就不知道使用哪種語言解釋執(zhí)行腳本。

一個典型的bash 以釋伴shebang行如下所示:

  1. #!/bin/bash

與本文中其他建議不同,這不僅僅是一條建議,而是一條規(guī)定。shell腳本必須以解釋器行開始;沒有這行,你的腳本最終將不能工作。我發(fā)現(xiàn)很多腳本沒有這一行,有人認(rèn)為沒有這行腳本就不能工作,但事實并非如此。如果沒有指定腳本解釋器,有些系統(tǒng)會默認(rèn)使用/bin/sh目錄下的解釋器。如果是bourne shell腳本,默認(rèn)/bin/sh路徑?jīng)]有問題,如果是KSH或者使用特定bash腳本而不是bourne,該腳本可能產(chǎn)生無法預(yù)料的結(jié)果。

添加腳本描述頭

當(dāng)編寫腳本或者其他程序時,我總會在腳本開頭描述腳本的用途,同時添加我的名字。如果這些腳本是在工作中編寫,我還會加上工作郵箱以及腳本編寫日期。

下面是一個有腳本頭的例子:

1. `#!/bin/bash`
2. `### Description: Adds users based on provided CSV file`
3. `### CSV file must use : as separator`
4. `### uid:username:comment:group:addgroups:/home/dir:/usr/shell:passwdage:password`
5. `### Written by: Benjamin Cane - ben@example.com on 03-2012`

為什么要添加這些內(nèi)容?很簡單。這里的描述是為了向閱讀該腳本的人解釋腳本用途并提供他們需要了解的其他信息。添加名字和郵箱,閱讀該腳本的人如果有疑問就可以聯(lián)系上我并提問。添加日期,當(dāng)他們閱讀腳本時,至少知道該腳本是多久之前編寫的。日期還能觸動你的懷舊之情,當(dāng)發(fā)現(xiàn)自己很久前編寫的腳本時,你會問問自己“在編寫該腳本時,我是怎么想的?”。

腳本中的描述頭可以根據(jù)自己的想法隨意定制,沒有硬性規(guī)定哪些是必須的,哪些不需要。通常只要保證信息有效并且放置在腳本開頭即可。

縮進(jìn)代碼

代碼可讀性非常重要,但很多人都會忽略這一點。在深入了解縮進(jìn)為何很重要前,我們來看一個例子:

1. `NEW_UID=$(echo $x |cut-d:-f1)`
2. `NEW_USER=$(echo $x |cut-d:-f2)`
3. `NEW_COMMENT=$(echo $x |cut-d:-f3)`
4. `NEW_GROUP=$(echo $x |cut-d:-f4)`
5. `NEW_ADDGROUP=$(echo $x |cut-d:-f5)`
6. `NEW_HOMEDIR=$(echo $x |cut-d:-f6)`
7. `NEW_SHELL=$(echo $x |cut-d:-f7)`
8. `NEW_CHAGE=$(echo $x |cut-d:-f8)`
9. `NEW_PASS=$(echo $x |cut-d:-f9)`
10. `PASSCHK=$(grep-c ":$NEW_UID:"/etc/passwd)`
11. `if[ $PASSCHK -ge 1]`
12. `then`
13. `echo"UID: $NEW_UID seems to exist check /etc/passwd"`
14. `else`
15. `useradd-u $NEW_UID -c "$NEW_COMMENT"-md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER`
16. `if[!-z $NEW_PASS ]`
17. `then`
18. `echo $NEW_PASS |passwd--stdin $NEW_USER`
19. `chage -M $NEW_CHAGE $NEW_USER`
20. `chage -d 0 $NEW_USER`
21. `fi`
22. `fi`

上述代碼能工作嗎?是的,但這段代碼寫的并不好,如果這是一個500行bash腳本,沒有任何縮進(jìn),那么理解該腳本的用途將非常困難。下面看一下使用縮進(jìn)后的同一段代碼:

1. `NEW_UID=$(echo $x |cut-d:-f1)`
2. `NEW_USER=$(echo $x |cut-d:-f2)`
3. `NEW_COMMENT=$(echo $x |cut-d:-f3)`
4. `NEW_GROUP=$(echo $x |cut-d:-f4)`
5. `NEW_ADDGROUP=$(echo $x |cut-d:-f5)`
6. `NEW_HOMEDIR=$(echo $x |cut-d:-f6)`
7. `NEW_SHELL=$(echo $x |cut-d:-f7)`
8. `NEW_CHAGE=$(echo $x |cut-d:-f8)`
9. `NEW_PASS=$(echo $x |cut-d:-f9)`
10. `PASSCHK=$(grep-c ":$NEW_UID:"/etc/passwd)`
11. `if[ $PASSCHK -ge 1]`
12. `then`
13. `echo"UID: $NEW_UID seems to exist check /etc/passwd"`
14. `else`
15. `useradd-u $NEW_UID -c "$NEW_COMMENT"-md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER`
16. `if[!-z $NEW_PASS ]`
17. `then`
18. `echo $NEW_PASS |passwd--stdin $NEW_USER`
19. `chage -M $NEW_CHAGE $NEW_USER`
20. `chage -d 0 $NEW_USER`
21. `fi`
22. `fi`

縮進(jìn)后,很明顯第二個if語句內(nèi)嵌在第一個if語句內(nèi),但如果看未縮進(jìn)的代碼,第一眼肯定發(fā)現(xiàn)不了。

縮進(jìn)方式取決于你自己,是使用兩個空格、四個空格,還是就使用一個制表符,這都不重要。重要的是代碼每次以相同的方式一致縮進(jìn)。

增加間距

縮進(jìn)可以增加代碼的可理解性,而間距可以增加代碼的可讀性。通常,我喜歡根據(jù)代碼的用途來間隔代碼,這是個人偏好,其意義在于使代碼更加可讀并易于理解。

下面是上述代碼添加行間距后的例子:

1. `NEW_UID=$(echo $x |cut-d:-f1)`
2. `NEW_USER=$(echo $x |cut-d:-f2)`
3. `NEW_COMMENT=$(echo $x |cut-d:-f3)`
4. `NEW_GROUP=$(echo $x |cut-d:-f4)`
5. `NEW_ADDGROUP=$(echo $x |cut-d:-f5)`
6. `NEW_HOMEDIR=$(echo $x |cut-d:-f6)`
7. `NEW_SHELL=$(echo $x |cut-d:-f7)`
8. `NEW_CHAGE=$(echo $x |cut-d:-f8)`
9. `NEW_PASS=$(echo $x |cut-d:-f9)`
10. `PASSCHK=$(grep-c ":$NEW_UID:"/etc/passwd)`
11. `if[ $PASSCHK -ge 1]`
12. `then`
13. `echo"UID: $NEW_UID seems to exist check /etc/passwd"`
14. `else`
15. `useradd-u $NEW_UID -c "$NEW_COMMENT"-md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER`
16. `if[!-z $NEW_PASS ]`
17. `then`
18. `echo $NEW_PASS |passwd--stdin $NEW_USER`
19. `chage -M $NEW_CHAGE $NEW_USER`
20. `chage -d 0 $NEW_USER`
21. `fi`
22. `fi`

如你所見,行間距雖不易覺察,但每一處整潔都讓以后的代碼排錯更簡單。

注釋代碼

描述頭適合于添加腳本函數(shù)描述,而代碼注釋適合于解釋代碼本身的用途。下面仍是上述相同的代碼片段,但這次我將添加代碼注釋,解釋代碼的用途:

1. `### Parse $x (the csv data) and put the individual fields into variables`
2. `NEW_UID=$(echo $x |cut-d:-f1)`
3. `NEW_USER=$(echo $x |cut-d:-f2)`
4. `NEW_COMMENT=$(echo $x |cut-d:-f3)`
5. `NEW_GROUP=$(echo $x |cut-d:-f4)`
6. `NEW_ADDGROUP=$(echo $x |cut-d:-f5)`
7. `NEW_HOMEDIR=$(echo $x |cut-d:-f6)`
8. `NEW_SHELL=$(echo $x |cut-d:-f7)`
9. `NEW_CHAGE=$(echo $x |cut-d:-f8)`
10. `NEW_PASS=$(echo $x |cut-d:-f9)`
11. `### Check if the new userid already exists in /etc/passwd`
12. `PASSCHK=$(grep-c ":$NEW_UID:"/etc/passwd)`
13. `if[ $PASSCHK -ge 1]`
14. `then`
15. `### If it does, skip`
16. `echo"UID: $NEW_UID seems to exist check /etc/passwd"`
17. `else`
18. `### If not add the user`
19. `useradd-u $NEW_UID -c "$NEW_COMMENT"-md $NEW_HOMEDIR -s $NEW_SHELL -g $NEW_GROUP -G $NEW_ADDGROUP $NEW_USER`
20. `### Check if new_pass is empty or not`
21. `if[!-z $NEW_PASS ]`
22. `then`
23. `### If not empty set the password and pass expiry`
24. `echo $NEW_PASS |passwd--stdin $NEW_USER`
25. `chage -M $NEW_CHAGE $NEW_USER`
26. `chage -d 0 $NEW_USER`
27. `fi`
28. `fi`

如果你恰好要閱讀這段bash代碼,卻又不知道這段代碼的用途,至少可以通過查看注釋充分掌握代碼的實現(xiàn)目標(biāo)。在代碼中添加注釋對其他人非常有幫助,甚至對你自己也有幫助。我曾發(fā)現(xiàn)在瀏覽自己一個月前編寫的腳本時不知道腳本的用途。如果注釋添加合理,可以在日后節(jié)省你和他人的很多時間。

創(chuàng)建描述性的變量名

描述性變量名非常直觀,但我發(fā)現(xiàn)自己一直都使用通用變量名。通常這些都是臨時變量,從不在該代碼塊之外使用,但即使是臨時變量,解釋清楚它們的含義也很有用。

下面例子中的變量名大部分是描述性的:

1. `for x in`cat $1``
2. `do`
3. `NEW_UID=$(echo $x |cut-d:-f1)`
4. `NEW_USER=$(echo $x |cut-d:-f2)`

可能賦給和NEW_USER的值不是很明顯,的值代表什么以及x的取值是什么都不夠清楚。更具描述性的修改代碼如下:

1. `INPUT_FILE=$1`
2. `for CSV_LINE in`cat $INPUT_FILE``
3. `do`
4. `NEW_UID=$(echo $CSV_LINE |cut-d:-f1)`
5. `NEW_USER=$(echo $CSV_LINE |cut-d:-f2)`

從這段重寫的代碼塊中,很容易看出我們是在讀取一個輸入文件,該文件名是一個CSV文件。同時很容易看出我們從什么地方獲取新的UID和新的USER信息來存儲在和NEW_USER變量中。

上面的例子看上去有點大材小用,但日后會有人感謝你花費額外時間讓變量更具描述性。

使用 $(command) 進(jìn)行命令替換

如果你想創(chuàng)建一個變量,其值是其他指令的輸出,在bash中有兩種方式實現(xiàn)。第一種是將命令封裝在反引號中,如下所示:

  1. DATE=date +%F“

第二種是使用一個不同的語法:

  1. DATE=$(date+%F)

雖然兩者都正確,但我個人更喜歡第二種方法。這純粹是個人偏好,但我通常認(rèn)為$(command)句法比使用反引號更加明顯。假如你在挖掘上百行的bash代碼;你會發(fā)現(xiàn)隨著自己不斷閱讀,那些反引號有時看起來像是單引號。此外,有時單引號看起來像是反引號。最后,所有的建議都與偏好掛鉤。所以使用最適合你的,確保與你所選擇使用的方法一致。

在出錯退出前描述問題

上述示例可以讓代碼更加易于閱讀和理解,最后一條建議對在排錯過程前找到錯誤點非常有用。在腳本中添加描述性錯誤信息,可以在前期節(jié)省很多排錯時間。瀏覽下面的代碼,看看如何能使它更具描述性:

  1. if[-d $FILE_PATH ]
  2. then
  3. for FILE in $(ls $FILE_PATH/*)
  4. do
  5. echo "This is a file: $FILE"
  6. done
  7. else
  8. exit 1
  9. fi

該腳本首先檢查$FILE_PATH變量的值是否是一個目錄,如果不是,腳本將退出,并返回一個錯誤代碼1。雖然使用退出代碼能夠告訴其他腳本該腳本未成功執(zhí)行,但卻沒有給運行該腳本的人做出解釋。

我們讓代碼變得更加友好些:

  1. if[-d $FILE_PATH ]
  2. then
  3. for FILE in $(ls $FILE_PATH/*)
  4. do
  5. echo "This is a file: $FILE"
  6. done
  7. else
  8. echo "exiting... provided file path does not exist or is not a directory"
  9. exit 1
  10. fi

如果運行第一個代碼片段,你將得到大量輸出。如果你得不到輸出,你將不得不打開腳本文件查看哪些地方可能出錯。但如果你運行第二個代碼片段,你立刻就能知道是在腳本指定了無效路徑。僅添加一行代碼就省去了以后大量的排錯時間。

上述例子僅僅是我在編程時嘗試使用的技巧。我相信編寫整潔可讀的bash腳本還有其他很多好建議,如果你有任何建議,隨時在評論區(qū)回復(fù)。很高興能看到其他人提出來的技巧。


新聞標(biāo)題:編寫B(tài)ash腳本的8個建議
文章分享:http://www.5511xx.com/article/coghsop.html