Shell 腳本編程
同傳統的編程語言一樣,shell 提供了很多特性,這些特性可以使你的 shell 腳本編程更為有用。
創建 Shell 腳本
一個 shell 腳本通常包含如下部分:首行+注釋+內容
- 首行
第一行內容在腳本的首行左側,表示腳本將要調用的shell解釋器,內容如下:
#!/bin/bash
#!符號能夠被內核識別成是一個腳本的開始,這一行必須位於腳本的首行,/bin/bash 是 bash 程序的絕對路徑,在這里表示后續的內容將通過 bash 程序解釋執行。
- 注釋
注釋符號 # 放在需注釋內容的前面,如下:
#! /bin/bash
# my first shell script(注釋)
- 內容
可執行內容和 shell 結構
#! /bin/bash
echo "hello,world"
Shell 腳本的權限
一般情況下,默認創建的腳本是沒有執行權限的
# ll | grep bash
-rw-r--r--. 1 root root 56 11月 1 07:19 bash.sh
沒有權限不能執行,需要賦予可執行權限
# chmod +x bash.sh
# ll | grep bash
-rwxr-xr-x. 1 root root 56 11月 1 07:19 bash.sh
Shell 腳本的三種執行方式
-
1、輸入腳本的絕對路徑或相對路徑
注:相對路徑不能省略 ./
-
2、bash (或 sh) + 腳本
注:當腳本沒有 x 權限時,root 和文件所有者通過該方式可以正常執行
注:創建 bash 新環境並執行腳本,執行完釋放 bash 環境
-
**3、在腳本的路徑前再加 ". " 或 source **
注:第一種和第二種會創建一個 bash 環境,不同 bash 中的變量無法共享;但是第三種方式是在同一個 bash 里面執行的
Shell 變量
-
變量:是 shell 傳遞數據的一種方式,用來代表每個取值的符號名;當 shell 腳本需要保存一些信息時,如一個文件名或是一個數字,就把它存放在一個變量中
-
變量設置規則:
-
變量名稱可以由字母,數字和下划線組成,但是不能以數字開頭,環境變量名建議大寫,便於區分
-
在 bash 中,變量的默認類型都是字符串型,如果要進行數值運算,則必須指定變量類型為數值型。
-
變量用等號連接值,等號左右兩側不能有空格
-
- 變量的值如果有空格,需要使用單引號或者雙引號包括。
變量分類
Linux Shell 的變量分為用戶自定義變量、環境變量、位置參數變量和預定義變量
# set # 可以查看系統中存在的所有變量
-
系統變量:保存和系統操作環境相關的數據,如 $HOME、$PWD、$SHELL、$USER 等
-
位置參數變量:主要用來向腳本中傳遞參數或數據,變量名不能自定義,變量作用固定
-
預定義變量:是 Bash 中已經定義好的變量,變量名不能自定義,變量作用也是固定的
用戶自定義變量
用戶自定義的變量由字母或下划線開頭,由字母,數字或下划線序列組成,並且大小寫字母意義不同,變量名長度沒有限制。
-
設置變量
習慣上用大寫字母來命名變量,變量名以字母表示的字符開頭,不能用數字。
# name=zs
-
變量調用
在使用變量時,要在變量名前加上前綴 “$”
使用 echo 命令查看變量值。
# echo $name
- 變量賦值
- 定義變量時賦值:變量=值
# STR="hello world"
# A=9
2. 將一個命令的執行結果賦給變量
# A=`ls -la` # 反引號,運行里面的命令,並把結果返回給變量A
# A=$(ls -la) # 等價於反引號
# echo $A
# aa=$((4+5))
3. 將一個變量賦給另一個變量
# A=$STR
- 變量疊加
# aa=123
# cc="$aa"456 # 使用雙引號
# cc=${aa}456 # 使用{}
# cc='$aa'456 # 無法疊加變量
單引號和雙引號的區別:
雙引號:雙引號里的變量名會替換為變量值
單引號:會將所有特殊字符脫意,里面的內容會原樣全部輸出
# NUM=10 # SUM="$NUM hehe" # echo $SUM 10 hehe # SUM='$NUM hehe' # echo $SUM $NUM hehe
-
列出所有的變量
# set
-
刪除變量
# aa=22 # echo $aa # unset aa # set # echo $aa # readonly bb=2 # 聲明靜態的變量不能unset # unset bb -bash: unset: bb: cannot unset: readonly variable
作用域:用戶自定義的變量,作用域為當前的 shell 環境。
環境變量
用戶自定義變量只在當前的 shell 中生效,而環境變量會在當前 shell 和其所有子 shell 中生效。如果把環境變量寫入相應的配置文件,那么這個環境變量就會在所有的 shell 中生效。
export 變量名=變量值 # 申明環境變量
作用域:當前 shell 以及所有的子 shell
位置參數變量
變量 | 描述 |
---|---|
$n | n為數字,$0代表命令本身,$1-$9代表第一到第9個參數, 十以上的參數需要用大括號包含,如${10}。 |
$* | 代表命令行中所有的參數,把所有的參數看成一個整體。以"$1 $2 … $n"的形式輸出所有參數 |
$@ | 代表命令行中的所有參數,把每個參數區分對待。以"$1" "$2" … "$n" 的形式輸出所有參數 |
$# | 代表命令行中所有參數的個數。添加到shell的參數個數 |
-
shift 指令
參數左移,每執行一次,參數序列順次左移一個位置,$# 的值減1,用於分別處理每個參數,移出去的參數不再可用
$ 和 $@ 的區別*
$* 和 $@ 都表示傳遞給函數或腳本的所有參數
- 不被雙引號" "包含時,都以"$1" "$2" … "$n" 的形式輸出所有參數
- 被雙引號" "包含時
- "$*" 會將所有的參數作為一個整體,以"$1 $2 … $n"的形式輸出所有參數
- "$@" 會將各個參數分開,以"$1" "$2" … "$n" 的形式輸出所有參數
預定義變量
變量 | 描述 |
---|---|
$? | 執行上一個命令的返回值 執行成功,返回0,執行失敗,返回非0(具體數字由命令決定) |
$$ | 當前進程的進程號(PID),即當前腳本執行時生成的進程號 |
$! | 后台運行的最后一個進程的進程號(PID),最近一個被放入后台執行的進程 &(后台運行) |
read 命令
read [選項] 值
read -p(提示語句) -n(字符個數) -t(等待時間,單位為秒) –s(隱藏輸入)
運算符
// 無法正確運算(變量默認是字符串類型)
# num1=11
# num2=22
# sum=$num1+$num2
# echo $sum
格式 : expr m + n 或 $((m+n))
expr 命令:對整數型變量進行算術運算
# expr 3 + 5 // expr 運算符間要有空格
# expr 3 – 5
# echo `expr 10 / 3`
# expr 3 \* 10 // \ 是轉義符
計算(2 +3 )× 4 的值
# S=`expr 2 + 3`
# expr $S \* 4
# expr `expr 2 + 3` \* 4
# echo $(((2 + 3) * 4)) //推薦使用
$() 、${} 與 $(()) 的區別
-
$( ):用途和反引號``一樣,用來表示優先執行的命令(與反引號可讀性更高)
# echo $(ls -l)
-
${ } :獲取變量值 (與$相比變量更容易識別)
# echo ${PATH}
-
$((運算內容)) :適用於數值運算(比expr更簡潔)
# echo $((3+1*4))
條件測試
內置 test 命令
test 命令可用於測試表達式,支持測試的范圍包括:字符串比較,算術比較,文件存在性、屬性、類型等判斷。例如,判斷文件是否為空、文件是否存在、是否是目錄、變量是否大於5、字符串是否等於"longshuai"、字符串是否為空等等。在shell 中,幾乎所有的判斷都使用 test 命令實現。
內置 test 命令常用操作符號有兩種格式:
- test expression
- [ expression ] # expression表達式首尾都必須要有空格(推薦使用)
表達式的結果為真,則 test 的返回值為 0,否則為非 0
當表達式的結果為真時,則變量$?的值就為 0,否則為非 0
字符串測試:
- test str1 == str2 或 [ str1 == str2 ] #測試字符串是否相等 =
- test str1 != str2 或 [ str1 != str2 ] #測試字符串是否不相等
- test str1 或 [ str1 ] #測試字符串是否不為空,(不為空:true,為空:false)
- test -n str1 或 [ -n str1 ] #測試字符串是否不為空
- test -z str1 或 [ -z str1 ] #測試字符串是否為空
注意:程序世界中一般都用1表示true,0表示false。但0表示失敗的話,沒有具體詳細的失敗含義,比如失敗原因可能是輸入參數不合法,可能是數據不存在等等,使用0來涵蓋這些所有的異常原因不利於問題排查。
因此在 Bash 中,用 0 表示 true,非0 表示 false。我們可以使用1-255中的任何一個數字來表示某個具體的錯誤,1是一個普遍的錯誤,126意味着一個文件不能被執行,127意味着’找不到命令’等。
; 命令連接符號,可以連接多個命令一起執行
- && 邏輯與條件滿足,才執行后面的語句
# [ "hello" == "hello" ] && echo yes
- || 邏輯或,條件不滿足,才執行后面的語句
[ "hello" == "world" ] && echo yes || echo no
整數測試:
符號 | 運算符 |
---|---|
-eq | == |
-ge | >= |
-gt | > |
-le | <= |
-lt | < |
-ne | != |
- test int1 -eq int2 或 [ int1 -eq int2 ] # 測試整數是否相等 equals
- test int1 -ge int2 或 [ int1-ge int2 ] # 測試int1是否>=int2
- test int1 -gt int2 或 [ int1 -gt int2 ] # 測試int1是否>int2
- test int1 -le int2 或 [ int1 -le int2 ] # 測試int1是否<=int2
- test int1 -lt int2 或[ int1 -lt int2 ] # 測試int1是否<int2
- test int1 -ne int2 或 [ int1 -ne int2 ] # 測試整數是否不相等
# test 100 –ge 100;echo $?
# [ 100 -ge 100 ];echo $?
文件測試:
- test -d file 或 [ -d file ] # 指定文件是否目錄
- test –e file 或 [ -e file ] # 文件是否存在 exists
- test -f file 或 [ -f file ] # 指定文件是否常規文件
- test –L file [ -L file ] # 文件存在並且是一個符號鏈接
- test -r file [ -r file ] # 指定文件是否可讀
- test -w file [ -w file ] # 指定文件是否可寫
- test -x file [ -x file ] # 指定文件是否可執行
# test -d install.log #是否為目錄
# test –r install.log #文件是否可讀
# test –f xx.log ; echo $? #是否是常規文件
# [ -L service.soft ] && echo “is a link” #文件存在並且是一個符號鏈接
# test -L /bin/sh ;echo $? #文件存在並且是一個符號鏈接
# [ -f /root ] && echo “yes” || echo “no” #是否是常規文件
多重條件測試:
- 條件1 –a 條件2 (邏輯與:兩個都成立,則為真)
- 條件1 –o 條件2 (邏輯或 :只要有一個為真,則為真)
- !條件 (邏輯非:取反)
注意與 && 和 || 的區別,不要混淆在一起
num=100
[ $num -gt 50 -a $num -gt 100 ];echo $?
[ $num -gt 50 -o $num -gt 10 ];echo $?
流程控制語句
if/else命令
1, 單分支if條件語句
if [ 條件判斷式 ]
then
代碼塊
fi
或者
if [ 條件判斷式 ] ; then
代碼塊
fi
#!/bin/sh
if [ $USER == "root" ];then
echo "super man"
fi
單分支條件語句注意事項:
-
if 語句使用 fi 結尾,和一般語言使用大括號結尾不同
-
[ 條件判斷式 ] 就是使用 test 命令判斷,所以中括號和條件判斷式之間必須有空格
2,多分支if條件語句
if [ 條件判斷式1 ]
then
當條件判斷式1成立時,執行程序1
elif [ 條件判斷式2 ]
then
當條件判斷式2成立時,執行程序2
...省略更多條件
else
當所有條件都不成立時,最后執行此程序
fi
case 命令
case 命令是一個多分支的 if/else命令,case 變量的值用來匹配 value1,value2,value3 等等。匹配到后則執行跟在后面的命令直到遇到雙分號為止(;😉,case 命令以 esac 作為終止符。
for 循環
for 循環命令用來在一個列表條目中執行有限次數的命令。比如,你可能會在一個姓名列表或文件列表中循環執行同個命令。
- for 命令后緊跟一個自定義變量、一個關鍵字 in 和一個字符串列表(可以是變量)
- 第一次執行 for 循環時,字符串列表中的第一個字符串會賦值給自定義變量,然后執行循環命令,直到遇到done語句;
- 第二次執行 for 循環時,會右推字符串列表中的第二個字符串給自定義變量,依次類推,直到字符串列表遍歷完。
for 語法格式
- 第一種:
#!/bin/bash
for i in 1 2 3
do
echo $i
done
- 第二種:
for ((i = 0; i <= 5; i++))
do
echo "welcome $i times"
done
while 循環
while 命令根據緊跟其后的表達式來判斷是否執行 while 循環,當 表達式執行后的返回值為0時,則執行 while 循環語句塊,直到遇到 done 語句,然后再返回到 while 命令,判斷表達式的返回值,當得打返回值為非0時,則終止 while 循環。
while 語法格式
- 第一種(循環條件)
while expression
do
代碼
done
- 第二種(死循環)
while :
do
代碼
done
自定義函數
函數代表着一個或一組命令的集合,表示一個功能模塊,常用於模塊化編程。
以下是關於函數的一些重要說明:
-
在 shell 中,函數必須先定義再調用
-
使用 return value 來獲取函數的返回值
-
函數在當前 shell 中執行,可以使用腳本中的變量
函數定義格式
function 函數名()
{
命令1….
命令2….
return 返回值變量
}
注意:
-
如果函數名后沒有(),在函數名和 { 之間,必須要有空格以示區分
-
函數返回值,只能通過$? 系統變量獲得,可以顯示加:return 返回值,如果不加將以最后一條命令運行結果,作為返回值
腳本調試
- sh -x script:這將執行該腳本並顯示所有變量的值。
- set -x :在shell腳本里添加,對部分腳本調試
- sh -n script:不執行腳本只是檢查語法的模式,將返回所有語法錯誤。
- sh –v script:執行並顯示腳本內容