PS : 注意本文討論的是Bash,而不必定是/bin/sh所連接的那個shell。這裏出現的全部代碼片斷,默認在頂上都添加了#!/bin/bash
。shell
while (( $# )); do case $1 in -a*) # Error checking [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\ "number specifier" 1>&2; return 1; } printf %d "${1#-a}" &> /dev/null || { echo "bash:"\ "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2 return 1; } # Assign array of -aN elements [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) && shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\ "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; } ;; -v) # Assign single value [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" && shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\ "argument(s)" 1>&2; return 1; } ;; *) echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2 return 1 ;; esac done
若是你以爲閱讀上面的Bash代碼,就像閱讀段子同樣順暢,那麼是時候關掉這個頁面,去作別的更有意義的行爲,好比去喝個水什麼的。
若是你以爲上面的Bash代碼猶如鬼畫符,而且實際生活中不得不面對它,那麼就看下去吧。數組
正式開始正文內容。
想要在一篇文章裏,講述要看懂開篇代碼所需的全部知識點,這是蚍蜉撼樹的行爲。所以,本文將講且僅講Bash中操做變量的方法。因此,即便你看完了這篇文章,你多半仍是看不懂開篇的代碼。bash
不過看完這篇文章以後,你對Bash的變量操做會有更爲深刻的認識。並且更重要的是,Bash之於你,再也不是怎麼也看不清摸不透。下一次要寫腳本的時候,你也將更加堅決地下定決心 —— 人生苦短,我用Python/Ruby。spa
Bash變量只有兩種類型,字符串和數組。不過從嚴格意義上,Bash沒有變量類型。Bash中的變量,在運行的時候會被展開成其對應的值(字符串)。你能夠把它看作C/C++中的宏定義,或者一些模板語言中的佔位符。命令行
通常狀況下,變量經過=
賦值,注意=
兩邊不要留空格。有些好孩子,已經養成了符號兩端留空格的習慣,結果當開始寫Bash的時候,他們抓狂了。
要想訪問變量,只需在變量名前面添加$
,解釋器就會對它進行展開。若是該變量並不存在,解釋器會把它展開成「」。code
me=spacewander echo $me echo $who
做爲腳本語言,第一要義固然是要隨時隨地獲取到命令行輸入啦。索引
在Bash中,使用$1
能夠獲取命令行輸入的第一個參數,$2
能夠獲取命令行輸入的第2個參數,$3
能夠獲取命令行輸入的第......
你看,$1到$10000的用法就這麼交代完了。Bash仍是挺有邏輯的嘛。ip
順便一提,$0
獲取的腳本的名字(其實就是其餘語言中的第0個參數),$@
獲取全部的參數,$#
獲取參數的數目。記住@
和#
這兩個符號,在Bash這一神祕的符文體系中,前者表示所有參數,後者表示參數的數目。ci
假如Bash變量中含有空白字符,或者含有特殊字符,好比*
,展開後會污染到外面的字符串,結果就是Bomb。
好比element
Oops='*' # '*'解釋成全部匹配的文件名 echo $Oops # 因此須要加雙引號括起來 echo "$Oops" # 加單引號會怎樣呢? echo '$Oops'
上面的代碼值得一試。
另一種Bomb的多是,變量後面須要接其它字符串,好比$FRUITs
。若是想讓解釋器識別成$FRUIT
變量,而不是$FRUITs
,須要用花括號括起來,像${FRUIT}s
Bash中可使用兩種容器。
一種是數組,另外一種是關聯數組,相似於其餘語言中的Map/Hash/Dict。
聲明數組的經常使用語法: declare -a ARY
或者ARY=(1 2 3)
聲明關聯數組的惟一語法: declare -A MAP
賦值的語法:
直接ARY[N]=VALUE
,N能夠是數字索引也能夠是鍵。關聯數組可使用MAP=([x]=a [y]=b)
進行多項賦值,注意這是賦值的語句而不是聲明。
親測數組中的索引不必定要按順序來,你能夠先給2和3上的元素賦值。(一樣算是弱類型的Javascript也支持這種無厘頭賦值,這算通病麼?)
往現有數組批量添加元素:
ARY+=(a b c) MAP+=([a]=1 [b]=2)
取值:
${ARY[INDEX]} ${MAP[KEY]}
注意花括號的使用
${A[@]}
展開成全部的變量,而獲取數組長度使用 ${#A[@]}
切片:${ARY[@]:N:M}
N是offset而M是length
返回索引,至關於keys():${!MAP[@]}
試試下面的代碼:
declare -a ARY declare -A MAP MAP+=([a]=1 [b]=2) ARY+=(a b c) echo ${ARY[1]} echo ${MAP[a]} echo "${ARY[@]}" echo "${MAP[@]}" echo "${ARY[@]:0:1}" echo ${#ARY[@]} echo "${!MAP[@]}" ARY[4]=a echo ${ARY[@]} echo ${ARY[3]}
Bash中的變量變換,大致是${變量[操做符]}的形式
HI=HellO echo "$HI" # HellO echo ${HI^} # HellO echo ${HI^^} # HELLO echo ${HI,} # hellO echo ${HI,,} # hello echo ${HI~} # hellO echo ${HI~~} #hELLo
^
大寫,,
小寫, ~
大小寫切換
重複一次只修改首字母,重複兩次則應用於全部字母。
混着用會怎樣?echo ${HI^,^} # HellO
看來是不行的×_×
%xx
從後往前,開始匹配,移除匹配的內容%%xx
跟上面的差很少,不過這是貪婪匹配#xx
從前日後,開始匹配,移除匹配的內容##xx
跟上面的差很少,不過這是貪婪匹配
這個比較難理解,不過看下面幾個例子應該能明白了。
FILENAME=/home/spacewander/param.sh echo ${FILENAME%/*} # /home/spacewander echo ${FILENAME%%/*} # echo ${FILENAME#*/} # home/spacewander/param.sh echo ${FILENAME##*/} # param.sh
/MATCH/VALUE
替換第一個匹配的內容。//MATCH/VALUE
替換匹配的內容
echo ${FILENAME/home/office} # /office/spacewander/param.sh echo ${FILENAME//s/S} # /home/Spacewander/param.Sh
獲取變量(字符串)長度:${#FILENAME}
字符串切片:跟數組切片是一樣的語法,${STR:offset:len}
TEXT=這個程序充滿了BUG! echo ${TEXT:0:8} echo ${TEXT:4} # 你還可使用負數做爲offset,這時候就是從後往前算起。注意負數要用括號括起來,避免衝突。 echo ${TEXT:(-4)}
Bash中有一項特性,你能夠方便地檢查某個變量是否設置了,若是沒有設置,就賦予一個默認值。尤爲在處理環境變量的時候,這項特性會讓你感到欣慰。
語法是${VAR:=VALUE}
或者${VAR=VALUE}
。此外,還有一個類似的語法,${VAR:-VALUE}
和${VAR-VALUE}
。
下面展現下二者的區別
# expand to default variable echo ${NULL-"Not null"} # Not null echo ${NULL} # # set default variable echo ${NIL="Not nil"} # Not nil echo ${NIL} # Not nil
能夠看出,前者只是當變量不存在時,展開成指定的值。然後者在變量不存在時,將變量的值設置爲指定值。
最後介紹一個,當目標變量不存在時,指定報錯信息的語法。
echo ${TARGET?Not Found} # 當$TARGET不存在時,顯示TARGET: Not Found,並結束程序。