玩轉Bash變量

PS : 注意本文討論的是Bash,而不必定是/bin/sh所連接的那個shell。這裏出現的全部代碼片斷,默認在頂上都添加了#!/bin/bashshell

一門自帶混淆的語言

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的變量操做會有更爲深刻的認識。並且更重要的是,Bash之於你,再也不是怎麼也看不清摸不透。下一次要寫腳本的時候,你也將更加堅決地下定決心 —— 人生苦短,我用Python/Ruby。spa

嚴格意義上的Bash變量類型

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

展開,而後Bomb!

假如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,並結束程序。
相關文章
相關標籤/搜索