歡迎光臨
每天分享高質量文章

Linux Bash腳本15分鐘進階教程

這裡的技術技巧最初是來自谷歌的“Testing on the Toilet” (TOTT)。這裡是一個修訂和擴增版本。

腳本安全

我的所有bash腳本都以下麵幾句為開場白:

#!/bin/bash

set -o nounset

set -o errexit

這樣做會避免兩種常見的問題:

  1. 取用未定義的變數(預設值為“”)

  2. 執行失敗的命令被忽略

需要註意的是,有些Linux命令的某些引數可以強制忽略發生的錯誤,例如“mkdir -p” 和 “rm -f”。

還要註意的是,在“errexit”樣式下,雖然能有效的捕捉錯誤,但並不能捕捉全部失敗的命令,在某些情況下,一些失敗的命令是無法檢測到的。(更多細節請參考這個帖子。)

腳本函式

在bash里你可以定義函式,它們就跟其它命令一樣,可以隨意的使用;它們能讓你的腳本更具可讀性:

ExtractBashComments() {

   egrep “^#”

}

cat myscript.sh | ExtractBashComments | wc

comments=$(ExtractBashComments < myscript.sh)

還有一些例子:

SumLines() {  

# iterating over stdin – similar to awk      

   local sum=0

   local line=””

   while read line ; do

       sum=$((${sum} + ${line}))

   done

   echo ${sum}

}

SumLines < data_one_number_per_line.txt

log() {  

# classic logger

  local prefix=”[$(date +%Y/%m/%d\ %H:%M:%S)]: “

  echo “${prefix} [email protected]” >&2

}

log “INFO” “a message”

盡可能的把你的bash代碼移入到函式里,僅把全域性變數、常量和對“main”呼叫的陳述句放在最外層。

變數註解

Bash里可以對變數進行有限的註解。最重要的兩個註解是:

  1. local(函式內部變數)

  2. readonly(只讀變數)

# a useful idiom: DEFAULT_VAL can be overwritten

#       with an environment variable of the same name

readonly DEFAULT_VAL=${DEFAULT_VAL:-7}

myfunc() {

 

# initialize a local variable with the global default

  local some_var=${DEFAULT_VAL}

  …

}

這樣,你可以將一個以前不是只讀變數的變數宣告成只讀變數:

x=5

x=6

readonly x

x=7  

# failure

儘量對你bash腳本里的所有變數使用local或readonly進行註解。

用$()代替反單引號(`)

反單引號很難看,在有些字體里跟正單引號很相似。$()能夠內嵌使用,而且避免了轉義符的麻煩。

# both commands below print out: A-B-C-D

echo “A-`echo B-\`echo C-\\\`echo D\\\`\“”

echo “A-$(echo B-$(echo C-$(echo D)))”

用[[]](雙層中括號)替代[]

使用[[]]能避免像異常的檔案擴展名之類的問題,而且能帶來很多語法上的改進,而且還增加了很多新功能:

運算子 功能說明

|| 邏輯or(僅雙中括號里使用)

&& 邏輯and(僅雙中括號里使用)

< 字串比較(雙中括號里不需要轉移)

-lt 數字比較

= 字串相等

== 以Globbing方式進行字串比較(僅雙中括號里使用,參考下文)

=~ 用正則運算式進行字串比較(僅雙中括號里使用,參考下文)

-n 非空字串

-z 空字串

-eq 數字相等

-ne 數字不等

單中括號:

[ “${name}” \> “a” -o ${name} \< “m” ]

雙中括號

[[ “${name}” > “a” && “${name}” < “m”  ]]

正則運算式/Globbing

使用雙中括號帶來的好處用下麵幾個例子最能表現:

t=”abc123″

[[ “$t” == abc* ]]        

# true (globbing比較)

[[ “$t” == “abc*” ]]      

# false (字面比較)

[[ “$t” =~ [abc]+[123]+ ]]

# true (正則運算式比較)

[[ “$t” =~ “abc*” ]]      

# false (字面比較)

註意,從bash 3.2版開始,正則運算式和globbing運算式都不能用引號包裹。如果你的運算式里有空格,你可以把它儲存到一個變數里:

r=”a b+”

[[ “a bbb” =~ $r ]]        

# true

按Globbing方式的字串比較也可以用到case陳述句中:

case $t in

abc*)   ;;

esac

字串操作

Bash里有各種各樣操作字串的方式,很多都是不可取的。

基本用戶

f=”path1/path2/file.ext”

len=”${#f}” # = 20 (字串長度)

# 切片操作: ${:} or ${::}

slice1=”${f:6}”

# = “path2/file.ext”

slice2=”${f:6:5}”

# = “path2”

slice3=”${f: -8}”

# = “file.ext”(註意:”-“前有空格)

pos=6

len=5

slice4=”${f:${pos}:${len}}”

# = “path2”

替換操作(使用globbing)

f=”path1/path2/file.ext”

single_subst=”${f/path?/x}”  

# = “x/path2/file.ext”

global_subst=”${f//path?/x}”  

# = “x/x/file.ext”

# 字串拆分

readonly DIR_SEP=”/”

array=(${f//${DIR_SEP}/ })

second_dir=”${arrray[1]}”    

# = path2

刪除頭部或尾部(使用globbing)

f=”path1/path2/file.ext”

# 刪除字串頭部

extension=”${f#*.}”  # = “ext”

# 以貪婪匹配方式刪除字串頭部

filename=”${f##*/}”  # = “file.ext”

# 刪除字串尾部

dirname=”${f%/*}”    

# = “path1/path2”

# 以貪婪匹配方式刪除字串尾部

root=”${f%%/*}”      

# = “path1”

避免使用臨時檔案

有些命令需要以檔案名為引數,這樣一來就不能使用管道。這個時候?

# 下載並比較兩個網頁

diff

還有一個非常有用處的是”here documents”,它能讓你在標準輸入上輸入多行字串。下麵的’MARKER’可以替換成任何字詞。

# 任何字詞都可以當作分界符

command  << MARKER

${var}

$(cmd)

MARKER

如果文本里沒有內嵌變數替換操作,你可以把第一個MARKER用單引號包起來:

command << ‘MARKER’

no substitution is happening here.

$ (dollar sign) is passed through verbatim.

MARKER

內置變數

變數 說明

$0 腳本名稱

$n 傳給腳本/函式的第n個引數

$$ 腳本的PID

$! 上一個被執行的命令的PID(後臺運行的行程)

$? 上一個命令的退出狀態(管道命令使用${PIPESTATUS})

$# 傳遞給腳本/函式的引數個數

[email protected] 傳遞給腳本/函式的所有引數(識別每個引數)

$* 傳遞給腳本/函式的所有引數(把所有引數當成一個字串)

提示

使用$*很少是正確的選擇。

[email protected]能夠處理空格引數,而且引數間的空格也能正確的處理。

使用[email protected]時應該用雙引號括起來,像”[email protected]”這樣。

除錯

對腳本進行語法檢查:

bash -n myscript.sh

跟蹤腳本里每個命令的執行:

bash -v myscripts.sh

跟蹤腳本里每個命令的執行並附加擴充信息:

bash -x myscript.sh

你可以在腳本頭部使用set -o verbose和set -o xtrace來永久指定-v和-o。當在遠程機器上執行腳本時,這樣做非常有用,用它來輸出遠程信息。

什麼時候不應該使用bash腳本

  • 你的腳本太長,多達幾百行

  • 你需要比陣列更複雜的資料結構

  • 出現了複雜的轉義問題

  • 有太多的字串操作

  • 不太需要呼叫其它程式和跟其它程式管道交互

  • 擔心性能

這個時候,你應該考慮一種腳本語言,比如Python或Ruby。

參考

  • Advanced Bash-Scripting Guide:?http://tldp.org/LDP/abs/html/

  • Bash Reference Manual

 

來自:外刊IT評論

鏈接:http://www.vaikan.com/bash-scripting/

赞(0)

分享創造快樂