Bash程式設計(3) 命令列解析與擴充套件
阿新 • • 發佈:2019-01-03
[email protected]表示指令碼輸入的全部引數,在bash指令碼中,若[email protected]增加引號("[email protected]"),則包含空格的引數也會被保留,若不增加引號([email protected]),則包含空格的引數會被拆分。
例:
# sa指令碼內容如下: pre=: post=: printf "$pre%s$post\n" "[email protected]" #printf "$pre%s$post\n" [email protected] # 注意[email protected]增加引號和不加引號的區別 $ bash test.sh "a b" :a b: #:a: #:b:
1. 引號
對於單引號、雙引號、轉義字元開頭的空格,命令列解析時將不會被拆分。
$ sa \ this "is a" 'demonstration of' \ quotes\ and\ espaces ## quotes\ and\ espaces中會轉義空格 : this: :is a: :demonstration of: : : :quotes and espaces: $ ./sa "a double-quoted single quote, '" "a double-quoted double quote, \"" # 單引號中的轉義字元將失效,而雙引號中的轉義將有效 :a double-quoted single quote, ': :a double-quoted double quote, ": $ ./sa 'a single-quoted double quotation mark, "' :a single-quoted double quotation mark, ": $ ./sa "First argument "'still the first argument' :First argument still the first argument: $ echo '\'line1\'\n\'line2\'' # 單引號中直接巢狀單引號,會有問題,即使增加轉義符也無法正常執行 $ echo $'\'line1\'\n\'line2\'' # 增加$符號即可解決單引號中巢狀單引號的問題
2. 花括號
花括號作用於不帶引號、以逗號分隔的列表或序列。當作為bash指令碼的輸入引數時,每個元素作為獨立的引數。
$ ./sa {one,two,three} :one: :two: :three: $ ./sa {1..3} #bash3.0後增加 :1: :2: :3: $ ./sa {a..c} :a: :b: :c: $ ./sa pre{d,1}date # 花括號前後的字串將包含在括號中的每個引數中 :preddate: :pre1date: $ ./sa {{1..3},{a..c}} # 花括號可以巢狀 :1: :2: :3: :a: :b: :c: $ ./sa {1..3}{a..c} # 連續多個的花括號將會逐個遞迴擴充套件 :1a: :1b: :1c: :2a: :2b: :2c: :3a: :3b: :3c: $ ./sa {01..13..3} # 4.0版本的bash具有更高特性:數值序列字首加0、可以指定序列中的步長 :01: :04: :07: :10: :13: $ ./sa {a..h..3} # 也可應用於字母 :a: :d: :g:
3. 波浪號
$ ./sa ~ # 不帶引號的~表示當前使用者的家目錄 :/home/music: $ ./sa ~root # ~後帶使用者名稱,將表示該使用者的家目錄 :/root: $ ./sa "~" "~root" # 引號中的~將不會擴充套件 :~: :~root: $ dir=~root $ dir2="~root" $ ./sa "$dir" "$dir2" :/root: :~root: $ ./sa ~ws # ~後的使用者名稱若不存在,則不擴充套件 :~ws:
4. 引數和變數擴充套件
$ var=whatever $ ./sa "$var" :whatever: $ var=qwerty $ ./sa "${var}" :qwerty: # 通常情況下,{}可選,當位置引數大於9或變數明後緊跟字元時,需要增加{} $ first=Jane $ last=Johnson $ ./sa "$first_$last" #first_是有效變數名 :Johnson: $ ./sa "${first}_$last" :Jane_Johnson:
5. 算術擴充套件
$ ./sa "$(( 1 + 12 ))" "$(( 12 * 13 ))" "$(( 16 / 4 ))" "$(( 6 - 9 ))" "$(( 10 % 3 ))" :13: :156: :4: :-3: :1: $ ./sa "$(( 3 + 4 * 5 ))" "$(( (3 + 4) * 5 ))" :23: :35: # 將秒轉為天、小時、分鐘 secs_in_day=86400 secs_in_hour=3600 mins_in_hour=60 secs_in_min=60 days=$(( $1 / $secs_in_day )) secs=$(( $1 % $secs_in_day )) printf "%d:%02d:%02d:%02d\n" "$days" "$(( $secs / $secs_in_hour ))" \ "$(( ($secs / $mins_in_hour) % $mins_in_hour ))" "$(( $secs % $secs_in_min ))"
6. 命令替換
$ wc -l $( date +%Y-%m-%d ).log 1 2019-01-02.log $ wc -l `date +%Y-%m-%d`.log # ``與$()作用相同 1 2019-01-02.log
7. 分詞
# 單詞拆分基於內部欄位分隔符(IFS)的值,預設IFS的為s' \t\n' $ var="this is a multi-word value" $ ./sa $var "$var" :this: :is: :a: :multi-word: :value: :this is a multi-word value: # 當IFS具有其預設值或未設定時,任何預設IFS字元序列都將作為單個分隔符讀取 $ var=' spaced > out ' $ ./sa $var :spaced: :out: # 當IFS包含另一個字元和空格時如" :",該" :"可以拆分欄位,但每個分空白字元也可以拆分欄位,即":"單獨也會進行拆分 $ IFS=' :' $ var="query : uiop : :: er" $ ./sa $var :query: :uiop: :: :: :er: # 當IFS僅包含非空字元時,則IFS中的每個字元拆分欄位,且空格將保留 $ IFS=: $ var="qwery : uiop : :: er" $ ./sa $var :qwery : : uiop : : : :: : er:
8. 路徑擴充套件
# 命令列中若包含*, ?, [,則將被當做檔案匹配模式 $ ./sa h* # 匹配當前路徑下以h開頭的檔案 :hw: $ ./sa *e # 匹配當前路徑下以k結尾的檔案 :datafile: :errorfile: $ ./sa ?a* # ?表示僅匹配一個字元 :datafile: :sa: # [aceg]表示匹配a,c,e,g中的任意一個 # [h-o]表示匹配h到o中的任意字元 # [[:lower:]]表示匹配小寫字母
9. 程序替換
程序替換將為命令建立一個臨時檔案。<(commond)使命令的輸出像檔名一樣可用;>(commond)表示可以寫入的檔名。
# totalsize在迴圈外不可用 $ ls -l | > while read perms links owner group size month day time file > do > printf "%10d %s\n" "$size" "$file" > totalsize=$(( ${totalsize:=0} + ${size:-0} )) > done $ echo ${totalsize-unset} ## print "unset" if variable is not set # 命令替換可以使totalsize在迴圈外可用 $ while read perms links owner group size month day time file > do > printf "%10d %s\n" "$size" "$file" > totalsize=$(( ${totalsize:=0} + ${size:-0} )) > done < <(ls -l *) $ echo ${totalsize-unset} 12879
10. 解析選項
shell指令碼中以-連線的選項,可以使用內建的getops解析。格式為:getopts OPTSTRING var
例:parseopts指令碼
progname=${0##*/} ## 獲取指令碼名稱 # 預設值 verbose=0 filename= # 列出程式接收的選項列表,這些選項帶引數,以:連線 optstring=f:v # while迴圈呼叫getopts,直到命令無更多的選項 # 每個選項儲存在$opt中,對應的選項引數儲存在OPTARG while getopts $optstring opt do case $opt in f) filename=$OPTARG ;; # $OPTARG包含選項對應的引數 v) verbose=$(( $verbose + 1 )) ;; *) exit 1;; esac done # 命令列中移除選項 ## $OPTIND指向下一個,且不解析引數 shift "$(( $OPTIND - 1 ))" # 檢查是否有檔案傳入 if [ -n "$filename" ] then if [ $verbose -gt 0 ] then printf "Filename is %s\n" "$filename" fi else if [ $verbose -gt 0 ] then printf "No filename entered\n" >&2 fi exit 1 fi # 檢查檔案是否存在 if [ -f "$filename" ] then if [ $verbose -gt 0 ] then printf "Filename %s found\n" "$filename" fi else if [ $verbose -gt 0 ] then printf "File, %s, does not exist\n" "$filename" >&2 fi exit 2 fi # 如果選擇了verbose選項,列印保留在命令中的數值引數 if [ $verbose -gt 0 ] then printf "Number of arguments is %d\n" "$#" fi
執行命令結果如下:
$ ./parseopts $ echo $? 1 $ ./parseopts -v No filename entered $ echo $? 1 $ ./parseopts -x ./parseopts: illegal option -- x $ ./parseopts -vf qwerty Filename is qwerty File, qwerty, does not exist $ ./parseopts -vf qwerty -- -x Filename is qwerty File, qwerty, does not exist $ ./parseopts -vf ~/.bashrc .bashrc .bashrc-anaconda3.bak $ ./parseopts -vf ~/.bashrc -- -x Filename is /home/music/.bashrc Filename /home/music/.bashrc found Number of arguments is 1