本文介紹是學習 Shell 第一節,主要介紹 Shell 變數的定義與運算:
- 變數名和變數型別
echo命令和跳脫字元- 字串在單引號和雙引號下表現不同
{}用於限定字串的範圍- 關鍵字
unset和readonly
1 變數定義
Shell 中變數定義如下:
var_name=variable
其中,var_name是變數名,variable是變數值。變數名和變數值之間的=左右一定不能有空格!
1.1 變數名
Shell 變數名需滿足如下規則:
- 只能是字母、數字、下劃線,不能數字開頭
- 大小寫敏感
- 不要包含 Shell 關鍵字(可用
help命令獲取)
根據以上規則,合法的變數名:
hello
hello000
hello_world_00
_konichiwa
nihao_
非法變數名:
help # 关键字
ni hao # 有空格
9am # 数字开头
1.2 變數值
Shell 中變數值分字串和陣列兩種。
變數值來源有直接賦值和來自命令輸出兩種。
1.2.1 字串
字串是 Shell 中常見的資料型別。Shell 中沒有int bool各種型別。除了陣列(下面會介紹)之外都是字串。
下面所有變數都是字串:
a="xy"
b=1
c="1"
d="hello world"
e="x\n"
f='x'
g='x\n'
在定義字串的值時,可以新增引號來顯式確定範圍,實際結果中不會包含這個引號。需要注意單引號和雙引號的區別(下面會介紹)。
a="xy"
a='xy'
a=xy
上面三個定義中a的值都是xy(不帶引號)
當然,也可以用其它命令輸出作為變數值,用`cmd`(反引號,esc下面那個鍵)或者$(cmd)的方式:
x=`pwd`
x=$(pwd)
1.2.1 陣列
用圓括號()定義,元素之間用空格分開:
var_list=(1 2 3)
Shell 中的陣列類似 Python 中的 dict,有 key 和 value 之分,只是恰好這個 key 是整數。所以也可以如下定義陣列:
var_list=(0=[hhh] 1=[2])
陣列的元素是下面介紹的字串。Shell 只支援一維陣列,不支援高維陣列。
當然,Shell 中陣列也可以來自命令輸出,用`cmd`(反引號,esc下面那個鍵)或者$(cmd)的方式:
a=`ls -a`
b=$(ls -a)
echo 'files are `ls -a`'
echo "files are `ls -a`"
echo "files are $(ls -a)"
1.3 變數引用
定義一個變數後,在其它地方要使用這個變數的值。
1.3.1 echo命令
在介紹 Shell 中變數引用之前,先介紹 Shell 中的echo命令,它的作用是列印變數值。(類似 Python 中的print和 C++中的printf)。
echo "haha" # haha
echo命令也有參數:加上-e則有轉義,加上-E參數則沒有轉義(預設無轉義)。e是 escape 的意思。Shell 中跳脫字元如下:
| 跳脫字元 | 含義 |
|---|---|
\\ | 反斜槓 |
\a | 警報,響鈴 |
\b | 退格(刪除前面一個字元) |
\f | 換頁(FF),將當前位置移到下頁開頭 |
\n | 換行 |
\r | 回車 |
\t | 水平製表符(tab 鍵) |
\v | 垂直製表符 |
關於\n和\r的區別,可以參考另外一篇文章。
echo -e "x\n"
# x
#
echo -E "\n"
# \n
1.3.2 變數引用
- 引用已經定義好的變數:在變數名前面新增
$(定義變數或變數重新賦值時不加$) - 用
$獲取變數時候,可新增{}來顯式界定變數範圍
為說明這個問題,先介紹 Shell 中字串的拼接操作,將兩個字串寫在一起。
a=5
b=6
echo a # output a
echo $a$b # output 56
echo $a # output 5
echo ${a}x # output 5x
echo $ax # output None(no output)
大括號{}用來界定範圍,${a}x界定變數是a而不是ax。
Shell 中變數可以用引號括起來,實際內容不會包含引號。其中單雙引號並不能通用:
- 單引號中的內容,也不會計算變數值,是啥樣就是啥樣
- 雙引號中的內容,會先計算變數結果,然後輸出
a=5
echo 'a' # output a
echo "a" # output a
echo '$a' # output $a
echo "$a" # output 5
echo $a # output 5
1.3.3 變數替換
Shell 中對於沒有定義的變數強行 echo,則會啥也不輸出,也不會報錯。 為解決變數沒有定義造成了的問題,Shell 中有變數替換的語法,用於處理變數沒有引用時候的場景。
| 形式 | 說明 |
|---|---|
${var} | var的值 |
${var:-word} | var若被定義,則輸出var的值;var若為空,或者為定義或已被刪除(unset),返回word,但不改變var的值。 |
${var:=word} | var若被定義,則輸出var的值;var若為空或已被刪除(unset),返回word,並將var的值設定為 word。 |
${var:?message} | var若被定義,則輸出var的值;var若為空或已被刪除(unset),訊息message送到標準錯誤輸出。該操作可以用來檢測變數var是否可以被正常賦值。若此替換出現在 shell script 中,則 shell script 將停止執行。 |
${var:+word} | var若被定義,那麼返回word,但不改變 var 的值。如果var沒有被定義,則返回空。 |
1.4 變數unset和readonly
Shell 中定義一個變數後,可以通過unset的方式刪掉。
a=4
echo $a # output 4
unset a
echo $a # output None(no output)
在 Shell 中如果一個變數被刪除掉,再引用則會得到的 None 的返回。這和大多數程式語言不一樣,大多數程式語言在處理沒有定義的變數時會直接報錯。(Shell 這種機制也增加了 Shell script 的除錯難度。)
定義變數時候新增readonly關鍵字:
readonly x=5
x=6 # readonly variable
變數前新增readonly之後,是無法刪除掉的。
2 字串運算
Shell 變數都是陣列和字串。對於變數,除了獲取變數的值之外,還可以進行更多操作。常見的操作有:
- 字串求長度
- 字串拼接
- 字串擷取
- 字串比較
- 數字字串的運算
2.1 字串求長度
x="hello world"
echo ${#x} # 11
#是 shell 中表示註釋的符號,如果在一個變數前面,左右又被{}包圍,則是求長度的意思。
2.2 字串拼接
就是將多個字串合併為一個字串,直接排列即可。
x=5
y='u'
z=$x$y
echo $z # 5u
z=${x}${y}
echo $z # 5u
2.3 字串擷取
從原來的字串中按照一定規則取出來一部分。
2.3.1 按長度擷取
對變數x進行擷取:
${x:start:length}
- 從左往右擷取
start是開始的 index- Shell 中陣列最開始元素的 index 是 0
start可以是一個數,表示是開始的 index。start也可以是0-n樣子,則是從後往前找開始的字元
- 如果
length長度超出了整個字串結尾,則擷取start開始的所有字元
x="hello world"
y=${x:1:2} # 1 is the start index left to right, 2 is the length
echo $y # el
y=${x:0-5:2} # right to left
echo $y # wo
2.3.2 按特定字元擷取
#擷取右邊字元,%擷取左邊字元- 一個
#(或%)表示從左往右第一個,兩個表示從左往右最後一個 - 用
*表示任意字元
# 使用#截取从左往右的右边的字符
url="http://aoi.ai/index.html"
echo ${url#*/} # /aoi.ai/index.html,从左往右第一个右边的字符
echo ${url##*/} # index.html,从左往右最后一个右边的字符
str="---aa+++aa@@@"
echo ${str#*aa} # +++aa@@@
echo ${str##*aa} # @@@
# 使用%截取从左往右左边的字符
url="https://aoi.ai/index.html"
echo ${url%/*} # https://aoi.ai
echo ${url%%/*} # http:
str="---aa+++aa@@@"
echo ${str%aa*} # ---aa+++
echo ${str%%aa*} # ---
2.4 字串比較
2.4.1 test和if函式
可以在 Shell 中對字串進行比較。判斷的結果可以傳遞給if等函式。例子如下:
# use test function
if test "$a" = "$b";then
command_1
else
command_2
fi
# use [ ]
if [ "$a" = "$b" ]; then
command_1
else
command_2
fi
分號;並非必要。 test "$a" = "$b"就是用於比較的意思,比較的結果傳遞給if函式,如果test中的結果為真,則執行command_1,否則執行command_2。在實際使用中test可以用中括號[]來表示。
2.4.2 字串相等與不等
如果兩個字串完全一樣,則是相等;如果不一樣則不等。判斷相等用 = (左右有空格),判斷不等用 != (左右有空格)。
a=5
b=5
if [ $a = $b ]; then
echo "equal"
fi
在實際使用中,還會遇到雙等號(==)情況:
- 在 bash 中,單等號和雙等號是等效的
- 在其他 shell 中,則單等號表示相等
所以推薦的寫法是用單等號。
2.4.3 數字字串操作
在 Shell 中,雖然沒有特別的數字型別資料,但是如果字串符合整數形式,則可以按照數字來處理:除了可以比較相等與否,還可以比較大小:
| 含義 | 表示方式 |
|---|---|
| equal 等於 | -eq |
| not equal 不等於 | -ne |
| greater than 大於 | -gt |
| less than 小於 | -lt |
| greater equal 大於等於 | -ge |
| less equal 小於等於 | -le |
需要注意的是,這些運算左右一定要是整數,否則就會報錯。
a=3
b=2
if [ $a -eq $b ]; then
echo "$a equals $b"
else
echo "$a does not eqaul $b"
fi
2.4.4 數字四則運算
Shell 中沒有數字這種型別,數字也被當成字串處理。如果非要進行數字四則運算,則需要藉助額外的工具。
整數運算
使用expr函式,規則是expr var_a oprator var_b,常見整數運算子:
| 運算子 | 表示方式 |
|---|---|
| 整數加法 | + |
| 整數減法 | - |
| 整數乘法 | \* |
| 整數求模 | / |
| 整數取餘數 | % |
| 左括號 | \( |
| 右括號 | \) |
- 1 求模就是整數除法,結果是整數,直接去掉後面的小數
expr -5 / -3 # 1 expr -5 / -3 # -1 expr 5 / -3 # -1 expr 5 / 3 # 1 - 2 求餘數以第一個變數符號為準
expr 5 % 3 # 2 expr 5 % -3 # 2 expr -5 % -3 # -2 expr -5 % 3 # -2 - 3 括號用於改變運算優先順序,變數之間要新增空格
expr \( 5 + 3 \) \* 2 - 4 疊加
expr運算子,用於連續計算expr $(expr 4 + 5) \* 8 # 72 expr `expr 4 + 5` \* 8 # 72
浮點運算
浮點運算要用bc:
echo 5.5 + 5.5 + 5.5 | bc # 16.5
echo 5.5 \* \( 5.5 + 5.5 \) | bc # 60.5
其中|是管道命令,將前一個命令的輸出作為後一個命令的輸入。新增scale可以設定保留的位數。
echo 5.5 / 10 | bc # 0
echo "scale=4; 5.5 / 10" | bc # 0.5500
5 陣列運算
3.1 陣列訪問
3.1.1 訪問某一個元素
用${a[index]},Shell 中陣列元素index從0開始。
a=(1 2 3)
echo "${a[2]}" # 3
index可以是正整數,表示從左往右數的第幾個數。如果index指代的索引不存在,則返回空。
a=(1 2 3)
echo "${a[4]}" # 输出一行空白,表示没有
當然,也可以像字串擷取一樣,使用0-n的形式,表示從右到左,此時從1開始記數:
a=(1 2 3)
echo "${a[0-1]}" # 3
3.1.2 陣列遍歷
要同時輸入陣列中的所有元素,可以用@或者*:
a=(1 3 5)
echo "${a[@]}" # 1 3 5
echo "${a[*]}" # 1 3 5
Shell 中還有一個用於遍歷訪問的函式for:
for i in ${a[@]};
do
command_1
done
其中a是一個陣列。in後面的物件不能是陣列,而要用${a[@]}或者${a[*]}的形式:
a=(1 2 3)
for i in ${a[@]};
do
echo $i
done
# 1
# 2
# 3
for i in ${a[*]};
do
echo $i
done
# 1
# 2
# 3
for i in $a;
do
echo $i
done
# 1
$a[*]和$a[@]都表示陣列中所有元素,它們有什麼區別呢:
- 不被雙引號(” “)包含時,都以
"$1" "$2" … "$n"的形式輸出所有參數。 - 當它們被雙引號(” “)包含時:
"$a[*]"會將所有的參數作為一個整體,以"$1 $2 … $n"的形式輸出所有參數"$a[@]"會將各個參數分開,以"$1""$2"…"$n"的形式輸出所有參數
a=(1 2 3)
for i in "${a[@]}";
do
echo $i
done
# 1
# 2
# 3
for i in "${a[*]}";
do
echo $i
done
# 1 2 3
藉助*和@符號,可以獲得陣列中所有元素。和獲獲取字串長度類似,也可以獲得整個陣列中的元素個數:
a=(1 2 3)
echo "${#a[@]}"
3.1.3 陣列擷取
意思是說取得陣列的部分,類似字串訪問:
a=(1 2 3)
echo "${a[@]:1:2}" # 2 3
echo "${a[@]:0-1:2}" # 3
3.2 陣列修改
修改陣列中某一個元素,用賦值的方式去修改(不要新增美元符號)。
a=(1 2 3)
echo "${a[1]}" # 2
a[1]=4
echo "${a[1]}" # 4
往陣列後面新增一個元素,只需要將index設定為陣列長度後面一個就行。
a=(1 2 3)
echo "${#a[@]}" # 3
a[3]=4
echo "${#a[@]}" # 4
a[8]=6
echo "${#a[@]}" # 5
這個index可以是任意正數。陣列a本身長度為3,此時可以設定a[3]的值,也可以設定a[5]的值。這個時候,陣列又有點類似 Python 的字典了,其中key就是index,如果index不存在則返回空。 雖然強行新增了a[8],但是整個陣列的長度還是 5。
3.3 陣列刪除
使用unset
a=(1 2 3)
unset a[0]
echo "${a[0]}" # 空白
3.4 陣列元素替換
陣列中的元素也可以替換,替換的是 value,不是 key。而且不會修改原來陣列。
a=(1 2 3)
echo "${a[@]/3/777}" # 1 2 777
echo "${a[@]}" # 1 2 3
總結
本文紹了 Shell 中變數定義和的運算:包含了字串的運算和陣列的運算,字串的運算中包括判斷是否相等,整數的四則運算,浮點數四則運算;陣列的運算包含了陣列的增刪查改。