目錄
條件測試操作
? ? ? ? 文件測試
? ? ? ? 整數值比較
? ? ? ? 字符串比較
? ? ? ? 邏輯測試
if 條件語句
? ? ? ? if語句的結構
? ? ? ? 1、單分支 if 語句
? ? ? ? 2、雙分支 if 語句
? ? ? ? 3、多分支 if 語句
? ? ? ? if語句應用實例
? ? ? ? 1、單分支 if 語句應用
? ? ? ? 2、雙分支 if 語句應用
? ? ? ? 3、多分支 if 語句應用
case 分支語句
? ? ? ? case語句的結構
? ? ? ? case語句應用實例
? ? ? ? 1、檢查用戶輸入的字符類型
? ? ? ? 2、編寫系統服務腳本
條件測試操作
????????要使 she11 腳本程序具備一定的“智能”,面臨的第一個問題就是如何區分不同的情況以確定執行何種操作。例如,當磁盤使用率超過 95%時,發送告警信息;當備份目錄不存在時,能夠自動創建;當源碼編譯程序時,若配置失敗則不再繼續安裝等。
????????shel1 環境根據命令執行后的返回狀態值($?)來判斷是否執行成功,當返回值為 8 時表示成功,否則(非 6 值)表示失敗或異常。使用專門的測試工具-test 命令,可以對特定條件進行測試,并根據返回值來判斷條件是否成立(返回值為8表示條件成立)。
? ? ? ? 使用test測試命令時,包括以下兩種形式。
test? 條件表達式
或
[? 條件表達式? ]
????????這兩種方式的作用完全相同,但通常后一種形式更為常用,也更貼近編程習慣。需要注意的是,方括號“[”或“]”與條件表達式之間需要至少一個空格進行分隔。
?????????根據需要測試的條件類別不同,條件表達式也不同。比較常用的條件操作包括文件測試、整數值比較、字符串比較,以及針對多個條件的邏輯測試,下面分別進行介紹。
? ? ? ? 文件測試
????????文件測試指的是根據給定的路徑名稱,判斷對應的是文件還是目錄,或者判斷文件是否可讀、可寫、可執行等。文件測試的常見操作選項如下,使用時將測試對象放在操作選項之后即可。
- -d:測試是否為目錄(Directory)。
- -e:測試目錄或文件是否存在(Exist)。
- -f:測試是否為文件(File)。
- -r:測試當前用戶是否有權限讀取(Read)。
- -w:測試當前用戶是否有權限寫入(write)。
- -x:測試是否設置有可執行(Excute)權限。
?????????執行條件測試操作以后,通過預定義變量$?可以獲得測試命令的返回狀態值,從而判斷該條件是否成立。例如,執行以下操作可測試目錄/media/是否存在,如果返回值$?為 ,表示存在此目錄,否則表示不存在或者雖然存在但不是目錄。
[root@localhost ~]#[ -d /media/ ]
[root@localhost ~]# echo $? //查看前一命令的返回值
0 //返回 0 表示條件成立
?????????若測試的條件不成立,則測試操作的返回值將不為 0(通常為 1)。例如,執行以下操作展示了測試不存在目錄的情況。
[root@localhost ~]#[ -d /media/cdrom/Server ]
[root@localhost ~]# echo $? //查看前一命令的返回值
1 //返回 1 表示條件不成立
?????????通過查看變量$?的值可以判斷前一步的條件測試結果,但是操作比較煩瑣,輸出結果也并不是很直觀。為了更直觀地査看測試結果,可以結合命令分隔符“&&”和 echo 命令一起使用,當條件成立時直接輸出“YES”。其中,“&8”符號表示“而且”的關系,只有當前面的命令執行成功后才會執行后面的命令,否則后面的命令將會被忽略。例如,上述目錄測試操作可以改寫如下。
[root@localhost ~]#[ -d /media/cdrom/Server ] && echo "YES"
//無輸出表示該目錄不存在[root@localhost ~]#[ -d /media/cdrom ]&& echo "YEs"
//輸出"YES"表示該目錄存在
YES
? ? ? ? 整數值比較
????????整數值比較指的是根據給定的兩個整數值,判斷第一個數與第二個數的關系,如是否大于、等于、小于第二個數。整數值比較的常用操作選項如下,使用時將操作選項放在要比較的兩個整數之間。
- -eq:第一個數等于(Equa1)第二個數。
- -ne:第一個數不等于(Not Equal)第二個數。
- -gt:第一個數大于(Greater Than)第二個數。
- -lt:第一個數小于(Lesser Than)第二個數。
- -le:第一個數小于或等于(Lesser or Equal)第二個數。
- -ge:第一個數大于或等于(Greater or Equal)第二個數。
?????????整數值比較在 She11 腳本編寫中的應用較多。例如,用來判斷已登錄用戶數量、開啟進程數、磁盤使用率是否超標,以及軟件版本號是否符合要求等。實際使用時,往往會通過變量引用、命令替換等方式來獲取一個數值。
? ? ? ? 如下:若要判斷當前已登錄的用戶數,當超過五個時輸出“Too many.”,可以執行以下操作。其中,已登錄用戶數可通過“who|wc -1”命令獲得,以命令替換方式嵌入。
[root@localhost ~]#Unum=`whowc -1` //查看當前已登錄用戶數
[root@localhost ~]#[ $Unum -gt 5 ]&& echo "Too many." //測試結果(大于)
Too many
????????又如,若要判斷物理內存(Mem)當前的磁盤緩存(buff/cache)大小,當低于 1024MB 時輸出具體數值,可以執行以下操作。其中,“free -m”命令表示以 MB 為單位輸出內存信息,提取的空閑內存數值通過命令替換賦值給變量 Freecc。
[root@localhost ~]# Freecc=$(free -m|grep "Mem:" awk '{print $6}')
[root@localhost ~]#[ $Freecc -lt 1024 ]&& echo ${Freecc}MB
275MB
? ? ? ? 字符串比較
????????字符串比較通常用來檢査用戶輸入、系統環境等是否滿足條件,在提供交互式操作的 shel1 腳本中,也可用來判斷用戶輸入的位置參數是否符合要求。字符串比較的常用操作選項如下。
- ?=:第一個字符串與第二個字符串相同。
- !=:第一個字符串與第二個字符串不相同,其中“!”符號表示取反。
- -z:檢查字符串是否為空(Zero),對于未定義或賦予空值的變量將視為空串。
?????????如下,若要判斷當前系統的語言環境,當發現不是“en.us”時輸出提示信息“Not en.us”,可以
共執行以下操作。
[root@localhost ~]# echO $LANG
//查看當前的語言環境
zh_CN.UTF-8[root@localhost ~]#[ $LANG != "en.us" ]&& echo "Not en.us"
//字符串測試結果(不等于)
Not en.Us
? ? ? ? 又如,?在 shel1 腳本應用中,經常需要用戶輸入“yes”或“no”來確認某個任務。以下操作展示了確認交互的簡單過程,當然,實際使用時還會根據變量“ACK”的取值分別執行進一步的操作。
[root@localhost ~]# read -p"是否覆蓋現有文件(yes/no)?" ACK
是否覆蓋現有文件(yes/no)?yes[root@localhost ~]#[ $ACK="yes"]&& echo“覆蓋”覆蓋
[root@localhost ~]# read -p"是否覆蓋現有文件(yes/no)?" ACK
是否覆蓋現有文件(yes/no)?no
[root@localhost ~]#[ $ACK="no"]&& echo"不覆蓋”不覆蓋
? ? ? ? 邏輯測試
????????邏輯測試指的是判斷兩個或多個條件之間的依賴關系。當系統任務取決于多個不同的條件時,根據這些條件是否同時成立或者只要有其中一個成立等情況,需要有一個測試的過程。常用的邏輯測試操作如下,使用時放在不同的測試語句或命令之間。
- &&:邏輯與,表示“而且”,只有當前后兩個條件都成立時,整個測試命令的返回值才為 0(結果成立)。使用 test 命令測試時,“&&”可改為“-a”。
- ||:邏輯或,表示“或者”,只要前后兩個條件中有一個成立,整個測試命令的返回值即為 0(結果成立)。使用 test 命令測試時,“|”可改為“-0”。
- !:邏輯否,表示“不”,只有當指定的條件不成立時,整個測試命令的返回值才為 0(結果成立)。
?????????在上述邏輯測試的操作選項中,“&&”和“|”通常也用于間隔不同的命令操作,其作用是相似的。實際上此前已經接觸過“&8”操作的應用,如“make&& make insta11”的編譯安裝操作。
????????如下,若要判斷當前 Linux 系統的內核版本是否大于 3.4,可以執行以下操作。其中,內核版本號通過 uname 和 awk 命令獲得。
[root@localhost ~]# uname -r //査看內核版本信息
3.10.0-514.e17.x86 64[root@localhost ~]# Mnum=$(uname -r| awk -F.'{print $1}')//取主版本號
[root@localhost ~]# Snum=$(uname -r|awk -f.'{print $2}')//取次版本號
[root@localhost ~]#[$Mnum-ge3]&&[$Snum -gt 4]&& echo"符合要求”符合要求
if 條件語句
????????實際上使用“&&”和“|”邏輯測試已經可以完成簡單的判斷并執行相應的操作,但是當需要選擇執行的命令語句較多時,這種方式將使執行代碼顯得很復雜,不好理解。而使用專用的 if 條件語句,可以更好地整理腳本結構,使得層次分明,清晰易懂。
? ? ? ? if語句的結構
????????在 she11 腳本應用中,if 語句是最為常用的一種流程控制方式,用來根據特定的條件測試結果,分別執行不同的操作(如果.……那么…)。根據不同的復雜程度,if 語句的選擇結構可以分為三種基本類型。適用于不同的應用場合。
? ? ? ? 1、單分支 if 語句
????????if 語句的“分支”指的是不同測試結果所對應的執行語句(一條或多條)。對于單分支的選擇結構,只有在“條件成立”時才會執行相應的代碼,否則不執行任何操作。單分支 if 語句的語法格式如下所示。
if 條件測試操作
then命令序列
fi
?????????在上述語句結構中,條件測試操作既可以是“[條件表達式]”語句,也可以是其他可執行的命令語句,命令序列指的是一條或多條可執行的命令行,也包括嵌套使用的 if 語句或其他流程控制語句。
????????單分支 if 語句的執行流程:首先判斷條件測試操作的結果,如果返回值為 0,表示條件成立,執行then 后面的命令序列,一直到遇見 fi 結束判斷為止,繼續執行其他腳本代碼;如果返回值不為 6,則忽略 then 后面的命令序列,直接跳至 fi 行以后執行其他腳本代碼,如下圖所示。
? ? ? ? 2、雙分支 if 語句
????????對于雙分支的選擇結構,要求針對“條件成立”“條件不成立”兩種情況分別執行不同的操作。雙分支 if 語句的語法格式如下所示。
if 條件測試操作
then命令序列 1
else命令序列 2
fi
?????????雙分支 if 語句的執行流程:首先判斷條件測試操作的結果,如果條件成立,則執行 then 后面的命令序列 1,忽略 else 及后面的命令序列 2,直到遇見 fi 結束判斷;如果條件不成立,則忽略 then 及后面的命令序列 1,直接跳至 else 后面的命令序列 2 并執行,直到遇見 fi 結束判斷,如下圖 所示。
? ? ? ? 3、多分支 if 語句
????????由于 if 語句可以根據測試結果的成立、不成立分別執行操作,所以能夠嵌套使用,進行多次判斷。例如,首先判斷某學生的得分是否及格,若及格則再次判斷是否高于 90 分等。多分支 if 語句的語法格式如下。
if 條件測試操作 1
then命令序列 1
elif 條件測試操作 2
then命令序列 2
else命令序列 3
fi
?????????上述語句結構中只嵌套了一個 elif 語句作為示例,實際上可以嵌套多個。if 語句的嵌套在編寫She11 腳本時并不常用,因為多重嵌套容易使程序結構變得復雜。當確實需要使用多分支的程序結構時,采用下一節的 case 語句更加方便。
????????多分支 if 語句的執行流程:首先判斷條件測試操作1 的結果,如果條件 1 成立,則執行命令序列1,然后跳至 fi 結束判斷;如果條件 1 不成立,則繼續判斷條件測試操作 2 的結果,如果條件 2 成立,則執行命令序列 2,然后跳至 fi 結束判斷……如果所有的條件都不滿足,則執行 else 后面的命令序列n,直到遇見 fi 結束判斷,如下圖所示。
? ? ? ? if語句應用實例
????????為了進一步理解 if 語句結構和流程,掌握 if 語句在 shel1 腳本中的實際使用,下面針對不同分支的 if 語句講解幾個腳本示例。
? ? ? ? 1、單分支 if 語句應用
????????很多 Linux 用戶習慣上將光盤設備掛載到/media/cdrom 目錄下,但 Linux 系統默認并沒有建立此目錄。若需要在 she11 腳本中執行掛載光盤的操作,建議先判斷掛載點目錄是否存在,若不存在則新建此目錄。
[root@localhost ~]# vim chkmountdir.sh
#!/bin/bash
MOUNT DIR="/media/cdrom"
if [ !-d $MOUNT DIR ]
thenmkdir -P $MOUNT_DIR
fi[root@localhost ~]# chmod +x chkmountdir.sh
[root@localhost ~]#./chkmountdir.sh
????????如有些特權命令操作要求以 root 用戶執行,如果當前用戶不是 root,那么再執行這些命令就沒有必要(肯定會失敗)。針對這種情況,在腳本中可以先判斷當前用戶是不是root,如果不是則報錯并執行“exit 1”命令退出腳本(1 表示退出后的返回狀態值),而不再執行其他代碼。?
[root@localhost ~]# vim /opt/chkifroot.sh
#!/bin/bash
if [ "$USER" !="root" ]
thenecho "錯誤!非 root 用戶,權限不足! "exit 1
fi
fdisk -l /dev/sda[root@localhost ~]# chmod +x /opt/chkifroot.sh
? ? ? ? ?當普通用戶執行 chkifroot.sh 腳本時,由于“非 root 用戶”的條件成立,因此會提示權限不足并退出腳本(使用“exit 1”退出腳本后,fi 之后的 fdisk 命令將不會執行)。
[root@localhost ~]# /opt/chkifroot.sh
錯誤!非 root 用戶,權限不足!
?????????當 root 用戶執行 chkifroot.sh 腳本時,由于“非 root 用戶”的條件不成立,所以 if 語句不執行任何操作,正常執行 fi 之后的腳本代碼。
[root@localhost ~]# /opt/chkifroot.sh
….//省略部分內容
Device Boot start End Blocks Id System
/dev/sda1 * 2048 40894463 20446208 83 Linux
/dev/sda2 40894464 41943039 524288 82 Linux swap / Solaris
? ? ? ? 2、雙分支 if 語句應用
?????????雙分支 if 語句只是在單分支的基礎上針對“條件不成立”的情況執行另一種操作,而不是“坐視不管”地不執行任何操作。例如,若要編寫一個連通性測試腳本 pinghost.sh,通過位置參數$1 提供目標主機地址,然后根據 ping 檢測結果給出相應的提示,可以參考以下操作過程。
[root@localhost ~]# vim pinghost.sh
#!/bin/bash
ping -c 3 -i 0.2 -W 3 $1 &>/dev/nul1 //將數據丟給黑洞了 /dev/null
-c 次數 -i 間隔時間 -W 超時 3秒
if [ $? -eq 0 ]
thenecho "Host $1 is up."
elseecho "Host $1 is down."
fi[root@localhost ~]# chmod +x pinghost.sh //給腳本執行權限
?????????在上述腳本代碼中,為了提高 ping 命令的測試效率,使用了“-c”“-씓-W”選項,分別指定只發送三個測試包、間隔 0.2 秒、超時 3 秒。另外,通過“&>/dev/nu11”屏蔽了 ping 命令執行過程的輸出信息。執行 pinghost.sh 腳本的效果如下所示。
[root@localhost ~]# ./pinghost.sh 192.168.10.101 //ping一個運行的主機
Host 192.168.10.101 is up.[root@localhost ~]# ./pinghost.sh 192.168.10.106 //ping一個沒有在運行的主機
Host 192.168.10.106 is down.
?????????例如,通過 shell 腳本檢査 vsftpd 服務是否運行,如果已經運行則列出其監聽地址、PID 號,否則輸出提示“警告:vsftpd 服務不可用!”。其中,pgrep 命令的“-x”選項表示査找時使用精確匹配。
[root@localhost ~]# vim chkvsftpd.sh
#!/bin/bash
systemctl status vsftpd &>/dev/nul1
if [ $? -eq 0 ]
thenecho"監聽地址:$(netstat -anpt|grep vsftpd | awk '{print $4}')"echo"進程 PID 號:$(pgrep -x vsftpd)"
elseecho"警告:vsftpd 服務不可用!"
fi[root@localhost ~]# chmod +x chkvsftpd.sh //給執行的權限
? ? ? ? 執行腳本的效果如下:
[root@localhost ~]# ./chkvsftpd.sh
警告:vsftpd 服務不可用!
[root@localhost ~]# systemctl start vsftpd
[root@localhost ~]# ./chkvsftpd.sh
監聽地址:::21
進程 PID 號:61003
? ? ? ? ?3、多分支 if 語句應用
????????與單分支、雙分支 if 語句相比,多分支 f 語句的結構能夠根據多個互斥的條件分別執行不同的操作,實際上等同于嵌套使用的 if 語句。例如,若要編寫一個成績分檔的腳本 gradediv.sh,根據輸入的考試分數不同來區分優秀、合格、不合格三擋,可以參考以下操作過程。
[root@localhost ~]# vim gradediv.sh
#!/bin/bash
read -p "請輸入您的分數(0-100):" GRADE
if [ $GRADE -ge 85 ]&& [ $GRADE -le 100 ]
thenecho“$GRADE 分,優秀!"
elif [ $GRADE -ge 70]&&[ $GRADE -le 84 ]
thenecho"$GRADE 分,合格!"
elseecho“$GRADE 分,不合格!"
fi[root@localhost ~]# chmod +x gradediv.sh //給腳本執行權限
? ? ? ? ? ? ? ? 執行腳本效果如下:
[root@localhost ~]# ./gradediv.sh請輸入您的分數(0-100):67
67 分,不合格![root@localhost ~]# ./gradediv.sh請輸入您的分數(0-100):78
78 分,合格![root@localhost ~]# ./gradediv.sh請輸入您的分數(0-100): 89
89 分,優秀!
case 分支語句
????????case 語句可以使腳本程序的結構更加清晰、層次分明,本節就來學習 case 語句的語法結構及應用。
? ? ? ? case語句的結構
????????case 語句主要適用于以下情況:某個變量存在多種取值,需要對其中的每一種取值分別執行不同的命令序列。這種情況與多分支的 if 語句非常相似,只不過 if 語句需要判斷多個不同的條件,而 case語句只是判斷一個變量的不同取值。
????????case 分支語句的語法結構如下所示:
case 變量 in
模式 1)命令序列 1;; //;;為結束分支
模式 2)命令序列 2;; //;;為結束分支
* )默認命令序列
esac
????????在上述語句結構中,關鍵字 case 后面跟的是“變量值”,即“$變量名”。整個分支結構包括在case..esac 之間,中間的模式 1、模式 2、…、*對應為變量的不同取值(程序期望的取值),其中*作為通配符,可匹配任意值。
?????????case 語句的執行流程:首先使用“變量值”與模式 1 進行比較,若取值相同則執行模式 1后的命令序列,直到遇見雙分號“;;”后跳轉至 esac,表示結束分支;若與模式 1 不相匹配,則繼續與模式 2進行比較,若取值相同則執行模式 2后的命令序列,直到遇見雙分號“;,”后跳轉至 esac,表示結束分支.…依此類推,若找不到任何匹配的值,則執行默認模式“*)”后的命令序列,直到遇見 esac 后結束分支,如下圖所示。
????????使用 case 分支語句時,有幾個值得注意的特點如下所述。
- case 行尾必須為單詞“in”,每一模式必須以右括號“)”結束。
- 雙分號“;;”表示命令序列的結束
- 模式字符串中,可以用方括號表示一個連續的范圍,如“[0-9]”;還可以用豎杠符號“|”表示或,如“A|B”。
- 最后的“*)”表示默認模式,其中的*相當于通配符。
? ? ? ? case語句應用實例
????????為了進一步理解 case 語句的結構和流程,掌握 case 語句在腳本中的實際使用,下面依次介紹兩個腳本示例。
? ? ? ? 1、檢查用戶輸入的字符類型
????????提示用戶從鍵盤輸入一個字符,通過 case 語句判斷該字符是否為字母、數字或者其他控制字符,并給出相應的提示信息。
[root@localhost ~]# vim hitkey.sh
#!/bin/bash
read -p“請輸入一個字符,并按 Enter 鍵確認:"KEY
case "$KEY" in[a-z]|[A-Z])//匹配任意字母echo“您輸入的是 字母,";;[0-9])//匹配任意數字echo"您輸入的是 數字.";;*) //默認模式,匹配任意字符echo"您輸入的是 空格、功能鍵或其他控制字符,"
esac[root@localhost ~]# chmod +x hitkey.sh //給腳本執行權限
????????測試并確認 hitkey.sh 腳本的執行結果如下所示
[root@localhost ~]# ./hitkey.sh
請輸入一個字符,并按 Enter 鍵確認:k //輸入字母 k
您輸入的是 字母。[root@localhost ~]# ./hitkey.sh
請輸入一個字符,并按 Enter 鍵確認:8 //輸入數字 8
您輸入的是 數字.[root@localhost ~]# ./hitkey.sh
請輸入一個字符,并按 Enter 鍵確認:^[[19~ //按 F8 鍵 //當輸入分支里沒有的就會報錯或者提示
您輸入的是 空格、功能鍵或其他控制字符.
? ? ? ? 2、編寫系統服務腳本
?????????編寫一個名為 myprog 的系統服務腳本,通過位置變量$1 指定的 start、stop、restart、status控制參數,分別用來啟動、停止、重啟 sleep 進程,以及査看 sleep 進程的狀態。其中,命令 sleep 用來暫停指定秒數的時間,這里僅用做測試,在實際運維工作中應將 sleep 改為相應后臺服務的控制命令序列。
[root@localhost ~]# vim myprog
#!/bin/bash
case "$1" in
start)echo -n "正在啟動 sleep 服務 ..."if sleep 7200 &then //在后臺啟動 sleep 進程echo "oK"fi;;
stop)echo -n "正在停止 sleep 服務 ..." pkill"sleep" &>/dev/nullecho "OK" //停止 sleep 進程;;
status)if pgrep "sleep"&>/dev/null ;then //判斷并提示 sleep 進程狀態echo "sleep 服務已經啟動," elseecho "sleep 服務已經停止,"fi;;
restart) //先停止、再啟動服務$0 stop$0 start;;
*) //默認顯示用法信息echo"用法:$0 {start|stop|statuslrestart}"
esac[root@localhost ~]# chmod +x myprog //給腳本執行權限
? ? ? ? 測試并 myprog 腳本的執行結果如下:
[root@localhost ~]# ./myprog start
正在啟動 sleep 服務...OK[root@localhost ~]# ./myprog status
sleep 服務已經啟動.[root@localhost ~]# ./myprog stop
正在停止 sleep 服務 ... OK、[root@localhost ~]# ./myprog reload
//未提供此參數,按默認處理
用法:./myprog {startlstop|status|restart}
?????????在 Linux 系統中,源碼軟件包編譯安裝后提供的服務控制腳本使用了 case 分支語句;也有一些源碼包沒有提供服務控制腳本,編譯安裝后可參照上例自行編寫服務控制腳本。平時控制各種系統服務時,供的 start、stop、restart 等位置參數,正是由 case 語句結構來識別并完成相應操作的。有興趣的同學可自行查閱這些腳本內容。
?????????若要將 myprog 服務交給 systemd 來管理,還需要在/lib/systemd/system 目錄下添加相應的myprog.service 配置文件。