接著上一章繼續
- 數值的對比
- 判斷語句
- 循環語句
22.5 比較、對比、判斷
在寫腳本時,有時需要做一些比較,例如,兩個數字誰大誰小,兩個字符串是否相同等。
做對比的表達式有[]、[[]]、test,其中[]和 test這兩種表達式的作用是相同的。[[]]和[]的不同
在于,[[]]能識別通配符和正則表達式中的元字符,[]卻不能。
需要注意的是,在比較時,中括號和后續提及的比較符兩邊都要留有空格。
22.5.1數字的比較
數字的比較,主要是比較兩個數字誰大誰小,或者是否相同。能用到的比較符有以下幾
種。
(1)-eq:相等。
(2)-ne:不相等。
(3)-gt:大于。
(4)-ge:大于等于。
(5)-lt:小于。
(6)-le:小于等于。
做完比較之后,通過返回值來判斷比較是否成立。
練習1:判斷1等于2,命令如下。
[root@pp yy]# [ 1 -eq 2 ]
[root@pp yy]# echo $?
1
[root@pp yy]#
1是不能等于2的,所以判斷不成立,返回值為非零。注意中括號和比較符兩邊的空格。
練習2:判斷1不等于2,命令如下。
[root@pp yy]# [ 1 -ne 2 ]
[root@pp yy]# echo $?
0
[root@pp yy]#
1不等于2,所以判斷成立,返回值為0
22.5.2 字待串的比較
字符串的比較,一般是比較兩個字符串是否相同,用得較多的比較符有以下兩種。
(1)==:相同。
(2)!=:不相同。
做完比較之后,通過返回值來判斷比較是否成立。
練習1:定義一個變量aa=tom,然后做判斷,命令如下。
[root@pp yy]# aa=tom
[root@pp yy]# [ $aa == tom ]
[root@pp yy]# echo $?
0
[root@pp yy]#
變量aa的值和 tom完全相同,所以判斷成立,返回值為0。
練習2:在判斷中匹配通配符,命令如下。
[root@pp yy]# aa=tom
[root@pp yy]# [ $aa == to? ]
[root@pp yy]# echo $?
1
[root@pp yy]#
這里定義aa=tom,按照前面講過的通配符,to?匹配的應該是前兩個字符為to,第三個
可以是任意字符,所以 tom應該會被to?匹配到,為什么返回值為非零呢?
原因在于在這一對中括號[]中是不能識別通配符的,aa的值是t、o、m三個字符,而等號
后面是t、o、?這三個字符,并沒有把問號當成通配符,所以判斷不成立。
如果想識別通配符,那么就要用雙中括號[[]],看下面的判斷。
[root@pp yy]# aa=tom
[root@pp yy]# [[[ $aa == to? ]]
[root@pp yy]# echo $?
0
[root@pp yy]#
在[[]]中能識別通配符“?”,所以這里判斷成立,返回值為0。
注意
(1)==后面跟的是通配符,如果想跟正則表達式,比較符就不能使用==了,要換成=~。
(2)一定要注意中括號和比較符兩邊的空格。
22.5.3 屬性的判斷
屬性的判斷,用于判斷一個文件是否具備某個屬性,常見的屬性包括以下7種。
(1)-r:具備讀權限。
(2)-w:具備寫權限。
(3)-x:具備可執行權限。
注意
以上三個屬性,不管是出現在u、g還是o上,只要有就算判斷成立。
()-d:一個目錄。
()-l:一個軟鏈接。
()-f:一個普通文件,且要存在。
()-e:不管什么類型的文件,只要存在就算判斷成立。
練習1:判斷/etc/hosts具備r權限,命令如下。
[root@pp yy]# ls -l /etc/hosts
-rw-r--r--. 1 root root 158 9月 10 2018 /etc/hosts
[root@pp yy]# [ -r /etc/hosts ]
[root@pp yy]# echo $?
0
[root@pp yy]#
通過第一條命令可以看到/etc/hosts是具備r權限的,判斷/etc/hosts具備r權限,自然成
立,所以返回值為0。
練習2:判斷/etc/hosts具備x權限,命令如下。
[root@pp yy]# [ -x /etc/hosts ]
[root@pp yy]# echo $?
1
[root@pp yy]#
22.5.4 使用連接符
前面講的判斷只是單個判斷,如果要同時做多個判斷,那么就需要使用連接符了。能用的
連接符包括“&&”和“||”。
先看一下使用&&作為連接符,用法如下。
1 判斷1 && 判斷2
只有兩個判斷都為真(返回值為0),整體才為真,只要有一個為假,整體就為假。判斷1
如果為假,判斷2還有必要執行嗎?沒有,因為整體已經確定為假了。判斷1為真,整體是真
是假在于判斷2,所以判斷2肯定是要執行的。
[root@pp yy]# [ 1 -le 2 ] && [ 2 -ge 3 ]
[root@pp yy]# echo $?
1
[root@pp yy]#
下面看使用||作為連接符,用法如下。
兩個判斷只要有一個為真(返回值為0),整體就為真,只有全都為假,整體才為假。
判斷1為真,整體已經確定為真,所以判斷2沒有必要執行。
判斷1為假,整體是真是假在于判斷2,所以判斷2肯定是要執行的。
[root@pp yy]# [ 1 -le 2 ] || [ 2 -ge 3 ]
[root@pp yy]# echo $?
0
[root@pp yy]#
這里有兩個判斷,第一個判斷是1小于等于2,這個判斷成立,整體已經確定為真,所以
整個判斷為真,返回值為0。
22.6 if判斷語句
在腳本中執行某條命令需要滿足一定的條件,如果不滿足就不能執行。此時我們就要用到
判斷語句了。
先看if判斷,if判斷的語法如下。
1 if 條件1 ; then
2 命令1
3 elif 條件2 ; then
4 命令2
5 else 命令3
6 fi
先判斷if后面的判斷是不是成立。
如果成立,則執行命令1,然后跳到f后面,執行6后面的命令。
如果不成立,則不執行命令1,然后判斷elif后面的條件2是不是成立。
如果成立,則執行命令2,然后跳到f后面,執行f后面的命令。
如果不成立,則不執行命令2,進行下一輪的elif 判斷,以此類推。
如果所有if和elif都不成立,則執行clse中的命令3。
練習1:寫一個腳本/opt/sc1.sh,要求只有root用戶才能執行此腳本,其他用戶不能執
行,命令如下。
[root@pp opt]# cat sc1.sh
#/bin/bash
if [ $UID ‐ne 0 ]; then
echo "只有root才能執行此腳本"
exit 1
fi
echo "hello root"
[root@pp opt]#
[root@pp opt]# chmod +x /opt/sc1.sh
腳本分析如下。
root的uid是0,其他用戶的uid不為0。第一個判斷,如果uid不等于0,則打印警告信
息“只有root才能執行此腳本”,然后exit退出腳本。
如果這里不加 exit,判斷之后仍然會繼續執行echo "hello root"命令,這樣判斷就失去
了意義。只有加了exit之后,如果不是root,則到此結束,不要繼續往下執行了。
如果是blab 執行此腳本,則判斷成立,打印完警告信息之后,通過exit退出腳本。
如果是 root執行此腳本,則判斷不成立,直接執行f后面的命令。
使用root用戶執行此腳本的結果如下。
[root@pp opt]# ./sc1.sh
hello root
[root@pp opt]#
使用iu用戶執行此腳本的結果如下。
[iu@pp opt]$ ./sc1.sh
只有root才能執行此腳本
[iu@pp opt]$
22.7 for循環語句
? ? ? 有時我們需要做多次重復的操作,例如,創建100個用戶,創建一個用戶需要兩條命
令:useradd和 passwd。那么,創建100個用戶就要重復執行100次,總共執行200條命令,
此時我們就可以利用for循環簡化操作,讓系統自動幫我們重復運行即可。
? ? ? ? for循環的語法如下。
1 for 變量 in 值‐1 值‐2 值‐3 值‐4 ; do
2 命令 $變量
3 done
這里首先把值-1賦值給變量,執行do和done之間的命令,所有命令執行完成之后,再把
值-2賦值給變量,執行do和done之間的命令,執行完所有命令之后,再把值-3賦值給變
量,以此類推,直到把所有的值都賦值給變量。
看一個簡單的例子,如下所示。
[iu@pp opt]$ for i in 1 2 3 4 ; do
> let i=$i+10
> echo $i
> done
11
12
13
14
[iu@pp opt]$
這里for后面定義了一個變量i,在in后面指定了4個值,分別是1、2、3、4。在do和done
之間定義了兩個命令,第一個是在變量i的原有值的基礎上加上10,然后打印i的值。
先把1賦值給i,此時i的值為1,執行do和 done之間的命令。i加上10之后,i的值變為了
11,然后打印i,得到11,第一次循環結束。
然后把2賦值給i,此時i的值為2,執行do和done之間的命令。i加上10之后,i的值變為了
12,然后打印i,得到12,第二次循環結束。
22.8 while 循環語句
while也可以循環,while循環的語法如下。
1 while 判斷 ; do
2 命令1
3 命令2
4 done
如果while后面的判斷成立,則執行do和 done之間的命令,在最后一個命令執行完成之
后,會回頭再次判斷一下while后面的判斷是不是成立。如果不成立,則跳出循環執行done后
面的命令;如果成立,則繼續執行do和 done之間的命令,就這樣循環下去。
先看一個簡單的例子,寫一個腳本/opt/sc3.sh,命令如下。
[root@pp opt]# cat sc3.sh
#!/bin/bash
declare ‐i n=1
while [ $n -le 4 ] ; do
echo $n
let n=$n+1
done
[root@pp opt]#
[root@pp opt]# chmod +x /opt/sc3.sh
腳本分析如下。
這里先通過declare -i n=1定義了一個整數類型的變量n,初始值為1。然后進入 while進
行循環,先判斷$n的值是不是小于等于4,如果成立,則執行do和 done之間的命令。
一開始$n的值為1,[ $n -le 4 ]這個判斷成立,則進人 do和done之間執行命令。首先打
印Sn的值,然后在此基礎上給n 加上1,所以n的值變為了2,這樣do和done之間的命令就 執行完成了。然后再次到while后面進行判斷,此時$n的值為2,依然滿足小于等于4,再次
執行do 和 done之間的命令。
如此反復,當$n的值最終能增加到4時打印,然后加1,此時n的值變為了5。當Sn的值變
為5之后,while后面的判斷就不再成立了,此時會跳出 while循環。
用while也可以用于循環一個文件的內容,用法如下。
1 while read aa ; do
2 命令
3 done < file
這里read后面的變量aa是可以隨意指定的,整體的意思是首先讀取file的第一行內容賦值
給aa,執行do和 done之間的命令。然后讀取file的第二行內容賦值給aa,執行do和done
之間的命令,直到讀取到file的最后一行。
有時while需要一直循環下去(死循環),語法如下。
1 while true ; do
2 命令
3 done
或
1 while ((1)) ; do
2 命令
3 done
或
1 while : ; do
2 命令
3 done
下面寫一個腳本,來實時判斷vsftpd是否啟動,如果沒有啟動,則將vsftpd啟動,命令如
下。
[root@pp opt]# cat sc4.sh
#!/bin/bash
while : ; do
systemctl is‐active vsftpd &> /dev/null
if [ $? -ne 0 ]; then
systemctl start vsftpd
fi
sleep 1
done
[root@pp opt]#
[root@pp opt]# chmod +x sc4.sh
這里寫了一個 while循環,可以一直循環下去,循環中先判斷vsftpd是否啟動,如果啟動
了則返回值為0,如果沒有啟動則返回值為非零。
下面開始根據返回值來進行判斷,如果$?不等于0,說明vsftpd沒有啟動,則啟動vsftpd
服務。sleep 1的意思是暫停1秒,這樣就實現了每隔1秒來判斷一次vsfilpd是否啟動。
下面開始測試這個腳本,先把腳本放在后臺運行,命令如下。
[root@pp opt]# ./sc4.sh &
[1] 3788
測試當前vsftpd 的狀態,命令如下。
[root@pp opt]# systemctl is‐active vsftpd
active
[root@pp opt]#
關閉vsftpd服務之后,再次檢測vsftpd 的狀態,命令如下。
[root@pp opt]# systemctl stop vsftpd
[root@pp opt]# systemctl is‐active vsftpd
active
[root@pp opt]#
可以看到,vsftpd 仍然是啟動的,說明我們的腳本生效了。 ?