如何使用bash寫腳本

本章主要介紹如何使用bash寫腳本。

  • 了解通配符
  • 了解變量
  • 了解返回值和數值運算
  • 數值的對比
  • 判斷語句
  • 循環語句

grep的用法是“grep 關鍵字 file”,意思是從file中過濾出含有關鍵字的行。?

? 例如,grep root /var/log/messages,意思是從/var/log/messages 中過濾出含有root 的行。這里很明確的是過濾含有“root”的行。???

? 如果想在/var/log/messages 中過濾出含有IP地址的行呢?IP地址就是一類字符,例如, 1.1.1.1是一個IP,192.168.26.100也是一個IP,那么用什么能表示出來這一類字符呢??

? 不管是通配符還是正則表達式,都是為了模糊匹配,為了匹配某一類內容,而不是具體的 某個關鍵字。通配符一般用在shell語言中,正則表達式一般用在其他語言中。?

? 不管是通配符還是正則表達式,主要是理解它們的元字符,然后用元字符來組合成我們想 要的那一類字符,本章主要講解通配符的使用。?

? 像我們平時說的張某某,這個某就是一個元字符,不是一個定值。指的是姓張,名字含有 2個字。張某某可能匹配到張二狗,也可能匹配到張阿貓,但是無法匹配到李阿三,也匹配 不了張三,因為張某某匹配的是姓名為3個字的,但是張三這個姓名只有2個字。?

? 如果說有一個人姓“張”名“某”,那么需要匹配“張某”這個人,而不是要匹配張三、 張四,可以用張\某,某前加個“\”表示轉義的意思。


通配符?

通配符一般用在shell語言中,通配符中常見的元字符如下。

  • [ ]:匹配一個字符,匹配的是出現在中括號中的字符。
  • ?[abc]:匹配一個字符,且只能是a或b或c。
  • [a-z]:“-”有特殊意義,表示“到”的意思,這里表示a~z,即匹配任一字母。
  • [0-9]:表示匹配任一數字。

如果想去除含有特殊意義的字符,前面加“\”表示轉義,即去除此字符的特殊意義。

  • [a\-z]:這里的“-”就沒有“到”的意思了,匹配的是“a”或“-”或“z”這三個 中的一個。

如果想表示“除了”的意思,則在第一個中括號后面加“!”或“^”。

  • [!a-z]、[^a-z]:表示除字母外的其他字符。
  • ?:表示一個任意字符,這里強調是一個,不是0個也不是多個,但不能匹配表示隱藏 文件的點。
  • *:表示任意多個任意字符,可以是0個,也可以是1個或多個,但不能匹配表示隱藏 文件的點。

練習:先創建目錄xx并在目錄中創建如下幾個測試文件,命令如下。

[root@rhel03 ~]# mkdir xx
[root@rhel03 ~]# cd xx
[root@rhel03 xx]# touch 1_aa aa11 Aa11 _aaa aa.txt f1aa u_12
[root@rhel03 xx]# 

找出首字符是字母、第二個字符是數字的文件,命令如下。?

[root@rhel03 xx]# ls [a-z][0-9]*
f1aa
[root@rhel03 xx]#

找出首字符是字母、第二個字符不是數字的文件,命令如下。?

[root@rhel03 xx]# ls [a-z][^0-9]*
aa11  Aa11  aa.txt  u_12
[root@rhel03 xx]#

找出首字符不是字母、第二個字符不是數字的文件,命令如下。

[root@rhel03 xx]# ls [^a-z][^0-9]*
1_aa  _aaa
[root@rhel03 xx]#

? 可以看到,找出來的文件完全符合我們的需求。下面找出首字符是大寫字母、第二個字符是非數字的文件,命令如下。?

[root@rhel03 xx]# ls [A-Z][!0-9]*
Aa11  u_12
[root@rhel03 xx]#

? 可以看到,首字符是大寫字母的文件列出來了,首字符是小寫字母的文件有的列出來了, 有的沒有列出來,所以[a-z]或[A-Z]有時并不精確。如果要更精確,可以用如下元字符。?

  • [[:upper:]]:純大寫。
  • [[:lower:]]:小寫。
  • [[:alpha:]]:字母。
  • [[:alnum:]:字母和數字。
  • [[:digit:]]:數字。?

列出首字符是小寫字母、第二個字符是數字的文件,命令如下。?

[root@rhel03 xx]# ls [[:lower:]][0-9]*
f1aa
[root@rhel03 xx]# 

列出首字符是大寫字母、第二個字符是數字或字母的文件,命令如下。?

[root@rhel03 xx]# ls [[:upper:]][[:alnum:]]*
Aa11
[root@rhel03 xx]#

如果想在yum 源中列出所有以 vsftpd開頭的包,可以用如下命令。?

[root@rhel03 ~]# yum list vsftpd*
正在更新 Subscription Management 軟件倉庫。
無法讀取客戶身份本系統尚未在權利服務器中注冊。可使用 subscription-manager 進行注冊。aa                                                                                                                       1.2 MB/s | 3.2 kB     00:00    
bb                                                                                                                       2.3 MB/s | 2.8 kB     00:00    
可安裝的軟件包
vsftpd.x86_64                                                               3.0.3-34.el8                                                               aa
[root@rhel03 ~]# 

在當前目錄中創建一個文件vsftpdxxx,命令如下。?

[root@rhel03 ~]# touch vsftpdxxx
[root@rhel03 ~]#

?然后再執行yum list vsftpd*命令,命令如下。

[root@rhel03 ~]# yum list vsftpd*
正在更新 Subscription Management 軟件倉庫。
無法讀取客戶身份本系統尚未在權利服務器中注冊。可使用 subscription-manager 進行注冊。上次元數據過期檢查:0:02:14 前,執行于 2023年12月10日 星期日 14時55分36秒。
錯誤:沒有匹配的軟件包可以列出
[root@rhel03 ~]# 

? 此處顯示沒有匹配的包,為什么呢?因為yum是 bash的一個子進程,vsftpd在 bash中首先被解析成了vsftpdxxx,然后再經過yum。所以,本質上執行的是yum list vsftpdxx命 令,而yum源中是沒有vsftpdxxx 這個包的,所以報錯。?

為了防止 bash 對這里的*進行解析,可以加上轉義符“\”,所以下面的命令是正確的。?

[root@rhel03 ~]# yum list vsftpd\*
正在更新 Subscription Management 軟件倉庫。
無法讀取客戶身份本系統尚未在權利服務器中注冊。可使用 subscription-manager 進行注冊。上次元數據過期檢查:0:03:11 前,執行于 2023年12月10日 星期日 14時55分36秒。
可安裝的軟件包
vsftpd.x86_64                                                               3.0.3-34.el8                                                               aa
[root@rhel03 ~]#

變量?

? 所謂變量,指的是可變的值,并非具體的值。例如,我自己嘴中發出的“我”,指的是我自己,張三嘴中發出的“我”,指的是張三,那么這個“我”就是一個變量。?

? 變量可以分為本地變量、環境變量、位置變量和預定義變量。?

(1)本地變量?

定義本地變量的格式如下。

  • 變量名=值?

定義變量有以下幾點需要注意。?

  1. 變量名可以包含_、數字、大小寫字母,但不能以數字開頭。
  2. “=”兩邊不要有空格。
  3. “值”如果含有空格,要使用單引號''或雙引號""引起來。
  4. “值”如果含有空格,要使用單引號''或雙引號""引起來。?

本章實驗都放在~/yy中練習,命令如下。?

[root@rhel03 ~]# mkdir yy ; cd yy
[root@rhel03 yy]#

下面開始練習定義變量,命令如下。?

[root@rhel03 yy]# 1aa=123
bash: 1aa=123: 未找到命令...
[root@rhel03 yy]# 

這里定義變量不正確,因為變量名不能以數字開頭,命令如下。?

[root@rhel03 yy]# aa-1=123
bash: aa-1=123: 未找到命令...
[root@rhel03 yy]# 

這里定義變量不正確,因為變量名只能是字母、數字、下劃線的組合,命令如下。?

[root@rhel03 yy]# aa =123
bash: aa: 未找到命令...
[root@rhel03 yy]# 

這里的錯誤是因為等號左邊有空格。?

[root@rhel03 yy]# aa=1 2
bash: 2: 未找到命令...
[root@rhel03 yy]#

這里的錯誤是因為“值”部分有空格沒有用引號引起來。?

[root@rhel03 yy]# aa=123
[root@rhel03 yy]# 

這里正確地定義了一個變量。

在使用本地變量時,變量名前需要加$,命令如下。?

[root@rhel03 yy]# echo $aa
123
[root@rhel03 yy]#

本地變量的特點是只能影響當前shell,不能影響子shell。?

[root@rhel03 yy]# echo $aa
123
[root@rhel03 yy]# echo $$
2092
[root@rhel03 yy]#

當前shell的PID是2092。下面打開一個子shell。?

[root@rhel03 yy]# bash
[root@rhel03 yy]# echo $$
2452
[root@rhel03 yy]#

這個子shell 的PID是2452。

[root@rhel03 yy]# echo $aa[root@rhel03 yy]#

可以看到,沒有aa變量。?

[root@rhel03 yy]# exit
exit
[root@rhel03 yy]# echo $$
2092
[root@rhel03 yy]# echo $aa
123
[root@rhel03 yy]# 

再次退回到原來的bash,又有了aa變量,情形如下圖所示。

?

定義變量除剛才顯式的定義外,還可以使用如下兩種方法。?

方法1:把一個命令的結果賦值給一個變量,這個變量要使用$()括起來,或者用反引號“引起來。這里是反引號,與波浪號~是同--個鍵,不是 單引號。?

例如,定義一個名稱是ip的變量,對應的值是ens160的IP,命令如下。?

[root@rhel03 yy]# ip=$(ifconfig ens160 | awk '/inet /{print $2}')
[root@rhel03 yy]# echo $ip
192.168.23.33
[root@rhel03 yy]#

方法2:通過read命令來獲取變量。

read的用法如下。

  • read ‐p "提示信息" 變量?

? 當遇到read命令時,系統會等待用戶輸入,用戶所輸入的值會賦值給read后面的變量, 命令如下。?

[root@rhel03 yy]# read -p "請輸入您的名字:" aa
請輸入您的名字:tom
[root@rhel03 yy]# echo $aa
tom
[root@rhel03 yy]# 

? 當執行read這條命令時,系統會提示用戶輸人一些內容,所輸入的內容會賦值給aa變量。這里我們輸入的是 tom,所以打印aa變量時,看到的值是tom。?

這樣的用法比較適合寫需要和用戶交互的腳本。


(2)環境變量?

? 定義環境變量的注意點和本地變量是一樣的。在定義環境變量時,前面加上export 即可, 命令如下。?

[root@rhel03 yy]# export bb=123
[root@rhel03 yy]#

或者先定義為本地變量,然后再通過export轉變為環境變量,命令如下。?

[root@rhel03 yy]# bb=123
[root@rhel03 yy]# export bb
[root@rhel03 yy]# 

要想查看所有的環境變量,可以執行env命令。

環境變量的特點是可以影響子shell,這里強調的是子shell,不能影響父shell。?

[root@rhel03 yy]# echo $$
2092
[root@rhel03 yy]# echo $bb
123
[root@rhel03 yy]#

當前shell的PID是2092,里面有一個環境變量 bb。?

[root@rhel03 yy]# bash
[root@rhel03 yy]# echo $$
2555
[root@rhel03 yy]# echo $bb
123
[root@rhel03 yy]# 

打開一個子shell,PID為2555,里面可以看到bb變量的值,說明環境變量已經影響到子shell 了?

[root@rhel03 yy]# export bb=456
[root@rhel03 yy]# exit
exit
[root@rhel03 yy]#

在子 shell中重新給bb賦值為456,然后退回到父shell。?

[root@rhel03 yy]# echo $$
2092
[root@rhel03 yy]# echo $bb
123
[root@rhel03 yy]# 

可以看到,在父shell 中,bb的值仍然是123,說明在子shell 中定義的變量不會影響到父 shell,如下圖所示。?

?

系統中默認已經存在很多個變量,如下所示。

  1. UID:表示當前用戶的uid。
  2. USER:表示當前用戶名。
  3. HOME:表示當前用戶的家目錄。

分別顯示這些變量的值,命令如下。?

[root@rhel03 yy]# echo $UID
0
[root@rhel03 yy]# echo $USER
root
[root@rhel03 yy]# echo $HOME
/root
[root@rhel03 yy]#

? 有一個很重要的環境變量PATH,當我們執行命令時,一定要指定這個命令的路徑,如果沒有寫路徑,則會到PATH變量所指定的路徑中進行查詢。先查看當前用戶的PATH變量,命令如下。?

[root@rhel03 yy]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@rhel03 yy]#

? PATH變量由多個目錄組成,每個目錄之間用冒號“:”分隔,我們把寫好的腳本放在 PATH變量指定的目錄中之后,運行此腳本時就不需要指定路徑了。?

查看tom用戶的PATH變量,命令如下。?

[tom@rhel03 ~]$ echo $PATH
/home/tom/.local/bin:/home/tom/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
[tom@rhel03 ~]$ 

? 這里展示了tom用戶的PATH 變量,如果tom寫了一個腳本之后,就可以把這個腳本放在 自己家目錄下的.local/bin或bin目錄下(~/.local/bin或~/bin)。如果這兩個目錄不存在, 創建出來即可。?

? 以上定義的環境變量也只是在當前終端中生效,關閉終端之后這個變量也就消失了。如果 想讓定義的變量永久生效,可以寫人家目錄的.bash_profile中。因為打開終端時,首先會運行家目錄下的一個隱藏文件.bash_profile。?


(3)位置變量和預定義變量?

? 運行腳本時,有時后面是需要加上參數的。但是我們在寫腳本時并不能預知后期在腳本后面跟上什么參數,這時就能用到位置變量了,位置變量如下。?

  • $0:表示腳本的名稱。
  • $1:表示第1個參數。
  • $2:表示第2個參數。
  • .........?
  • ${10}:表示第10個參數。?

這里$后面的數字如果不是個位數,則要用{}括起來。?

系統中還內置了一些預定義變量。?

  • $#:表示參數的個數。
  • $*:表示所有的參數。?

例1:寫一個帶參數的腳本,內容如下。?

[root@rhel03 yy]# cat scl.yaml 
#/bin/bash
echo "這是我的第一個腳本,腳本名稱是$0"
echo "第1個參數是:$1"
echo "第2個參數是:$2"
echo "第3個參數是:$3"
echo "此腳本一共有$#個參數,它們分別是:$*"[root@rhel03 yy]# 

給這個腳本加上可執行權限,并加參數運行,命令如下。?

[root@rhel03 yy]# chmod +x scl.yaml 
[root@rhel03 yy]# ./scl.yaml tom bob mary
這是我的第一個腳本,腳本名稱是./scl.yaml
第1個參數是:tom
第2個參數是:bob
第3個參數是:mary
此腳本一共有3個參數,它們分別是:tom bob mary
[root@rhel03 yy]# 

? 運行這個腳本時,共指定了3個參數:tom、bob、mary,它們分別賦值給了 $1、$2、$3。這里S#被自動賦值為3,因為總共有3個參數,所有的參數被賦值給$*。?

例2:運行如下命令。

[root@rhel03 yy]# set a b c d e f g h i j k
[root@rhel03 yy]#

查看此命令的第1個和第9個參數,命令如下。?

[root@rhel03 yy]# echo $1
a
[root@rhel03 yy]# echo $9
i
[root@rhel03 yy]# 

第1個參數是a,第9個參數是i。下面查看第10個參數,命令如下。?

[root@rhel03 yy]# echo $10
a0
[root@rhel03 yy]# 

? 第9個參數是i,那么第10個參數應該是j才對,這里顯示為a0,為什么呢?因為這里先把 $10當成了$1+0,$1的值是a,所以$10的值為a0。?

所以,在位置變量中數字超過10時,要用{}括起來,下面的命令才是正確的。?

[root@rhel03 yy]# echo ${10}
j
[root@rhel03 yy]#

另外,在引用變量時,雙引號和單引號是有區別的,直接看一個例子。?

[root@rhel03 yy]# xx=tom
[root@rhel03 yy]# echo "my name is $xx"
my name is tom
[root@rhel03 yy]# echo 'my name is $xx'
my name is $xx
[root@rhel03 yy]#

? 這里先定義一個變量xx=tom,如果變量在雙引號中引用,則會被解析成具體的值;如果變量出現在單引號中,則不被解析。?


返回值?

? 執行某命令之后,結果不是正確的就是錯誤的。命令正確執行了,返回值為0,如果沒有正確執行則返回值為非零。返回值為非零,不一定是語法錯誤,執行結果如果有“否定”的 意思,返回值也為非零。例如, ping 192.168.26.3,語法沒有錯誤,但是沒有ping通,返回值 也為非零。?

? 返回值記錄在$?中,且$?只記錄剛剛執行過命令的返回值。因為$?的值會被新執行命令的返回值覆蓋。?

練習:先執行一個 xxx命令,命令如下。?

[root@rhel03 yy]# xxxx
bash: xxxx: 未找到命令...
[root@rhel03 yy]# echo $?
127
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]#

? 先執行一個xxx命令,這個命令是錯誤的命令,$?記錄的是剛剛執行過xxx命令的返回值。 所以,查看$?的值是127,是一個非零的值。再次查看$?的值時,卻變成了0,因為這個$? 記錄的不再是xxx命令的返回值,而是它前面執行過的echo $?命令的返回值。?

邏輯上“否定”的意思也是可以體現出來的。例如,下面的例子。?

[root@rhel03 yy]# grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

這里在/etc/passwd過濾行開頭為root的行,結果找到了,所以返回值為0。?

[root@rhel03 yy]# grep ^rootxxx /etc/passwd
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]#

這里在/etc/passwd過濾行開頭為rootxxx的行,結果沒有找到,即使語法沒有錯誤,但是邏輯上有“否定”的意思,所以返回值為非零。


數值運算?

在寫腳本時,有時我們經常要做一些數學運算。數學運算的符號如下。?

  • +:表示加。
  • -:表示減。
  • *:表示乘。
  • /:表示除。
  • **:表示次方。?

進行數學運算的表達式有$(())、$[]、let等,命令如下。?

[root@rhel03 yy]# echo $((2+3))
5
[root@rhel03 yy]# echo $((2*3))
6
[root@rhel03 yy]# echo $((2**3))
8
[root@rhel03 yy]# echo $[2**3]
8
[root@rhel03 yy]# 

其中$(O)和$[]的用法是一樣的,如果不用這樣的表達式,看如下代碼。?

[root@rhel03 yy]# echo 2**3
2**3
[root@rhel03 yy]#

這里并不是計算的2的3次方,而是直接把這4個字符打印出來了。?

let也可以用于數學運算,命令如下。

[root@rhel03 yy]# let aa=1+2
[root@rhel03 yy]# echo $aa
3
[root@rhel03 yy]# 

這里aa的值就是為3。

下面來看不使用let的情況,命令如下。?

[root@rhel03 yy]# aa=1+2
[root@rhel03 yy]# echo $aa
1+2
[root@rhel03 yy]#

這里并沒有把aa的值1+2當成數字,而是當成了3個字符:“1”“+”“2”,所以結果顯示的也是1+2。?

可以實現定義aa為整數類型,然后再做數學運算,命令如下。

[root@rhel03 yy]# declare -i aa
[root@rhel03 yy]# aa=1+2
[root@rhel03 yy]# echo $aa
3
[root@rhel03 yy]# 

首先declare -i aa把aa定義為一個整數,所以1+2等于3,然后賦值給aa.所以aa的值為3。 以上表達式不能求得小數,如果要得到小數需要使用 bc 命令,用法如下。?

  • echo "scale=N ; 算法 | bc"?

這里N是一個數字,表示小數點后面保留幾位。

計算2/3,小數點后面保留3位,命令如下。?

[root@rhel03 yy]# echo "scale=3 ; 2/3" | bc
.666
[root@rhel03 yy]# 

這里得到的結果是0.666,整數部分的0沒有顯示。

計算7/6,小數點后面保留3位,命令如下。

[root@rhel03 yy]# echo "scale=3 ; 7/6" | bc
1.166
[root@rhel03 yy]# 

比較、對比、判斷?

? 在寫腳本時,有時需要做一些比較,例如,兩個數字誰大誰小,兩個字符串是否相同等。 做對比的表達式有[]、[[]]、test,其中[]和 test這兩種表達式的作用是相同的。[[]]和[]的不同 在于,[[]]能識別通配符和正則表達式中的元字符,[]卻不能。?

需要注意的是,在比較時,中括號和后續提及的比較符兩邊都要留有空格。


(1)數字的比較?

數字的比較,主要是比較兩個數字誰大誰小,或者是否相同。能用到的比較符有以下幾種。

  • -eq:相等。
  • -ne:不相等。
  • -gt:大于。
  • -ge:大于等于。
  • -lt:小于。
  • -le:小于等于。?

做完比較之后,通過返回值來判斷比較是否成立。?

練習1:判斷1等于2,命令如下。

[root@rhel03 yy]# [ 1 -eq 2 ]
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]# 

1是不能等于2的,所以判斷不成立,返回值為非零。注意中括號和比較符兩邊的空格。?

練習2:判斷1不等于2,命令如下。

[root@rhel03 yy]# [ 1 -ne 2 ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]#

1不等于2,所以判斷成立,返回值為0。?


(2)字待串的比較?

字符串的比較,一般是比較兩個字符串是否相同,用得較多的比較符有以下兩種。

  • ==:相同。
  • !=:不相同。?

做完比較之后,通過返回值來判斷比較是否成立。?

練習1:定義一個變量bb=tom,然后做判斷,命令如下。?

[root@rhel03 yy]# bb=tom
[root@rhel03 yy]# [ $bb == tom ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

變量bb的值和 tom完全相同,所以判斷成立,返回值為0。?

練習2:在判斷中匹配通配符,命令如下。

[root@rhel03 yy]# bb=tom
[root@rhel03 yy]# [ $bb == to? ]
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]# 

? 這里定義bb=tom,按照前面講過的通配符,to?匹配的應該是前兩個字符為to,第三個可以是任意字符,所以 tom應該會被to?匹配到,為什么返回值為非零呢??

? 原因在于在這一對中括號[]中是不能識別通配符的,bb的值是t、o、m三個字符,而等號后面是t、o、?這三個字符,并沒有把問號當成通配符,所以判斷不成立。?

如果想識別通配符,那么就要用雙中括號[[]],看下面的判斷。

[root@rhel03 yy]# bb=tom
[root@rhel03 yy]# [[ $bb == to? ]]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

在[[]]中能識別通配符“?”,所以這里判斷成立,返回值為0。?

注意:

  • ==后面跟的是通配符,如果想跟正則表達式,比較符就不能使用==了,要換成=~。
  • 一定要注意中括號和比較符兩邊的空格。?

(3)屬性的判斷?

屬性的判斷,用于判斷一個文件是否具備某個屬性,常見的屬性包括以下7種。

  • -r:具備讀權限。
  • -w:具備寫權限。
  • -x:具備可執行權限。

注意:以上三個屬性,不管是出現在u、g還是o上,只要有就算判斷成立。?

  • -d:一個目錄。
  • -l:一個軟鏈接。
  • -f:一個普通文件,且要存在。
  • -e:不管什么類型的文件,只要存在就算判斷成立。?

練習1:判斷/etc/hosts具備r權限,命令如下。

[root@rhel03 yy]# ls -l /etc/hosts
-rw-r--r--. 1 root root 185 12月  4 12:17 /etc/hosts
[root@rhel03 yy]# [ -r /etc/hosts ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]#

? 通過第一條命令可以看到/etc/hosts是具備r權限的,判斷/etc/hosts具備r權限,自然成立,所以返回值為0。?

練習2:判斷/etc/hosts具備x權限,命令如下。

[root@rhel03 yy]# [ -x /etc/hosts ]
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]# 

? 這里判斷/etc/hosts具備x權限,但是/etc/hosts不管是u、g還是o都不具備x權限,所以判斷不成立,返回值為非零。?

如果做一個否定判斷,在前面加上嘆號“!”即可。

練習3:判斷/etc/hosts沒有x權限,命令如下。?

[root@rhel03 yy]# [ ! -x /etc/hosts ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

這里判斷/etc/hosts沒有x權限,判斷是成立的,所以返回值為0。

練習4:判斷/etc是一個普通文件,命令如下。

[root@rhel03 yy]# [ -f /etc/ ]
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]# 

我們知道/etc是一個目錄而不是一個文件,所以這個判斷是不成立的,返回值為非零。?

練習5:判斷/etc不管是什么類型的,只判斷存在還是不存在,命令如下。

[root@rhel03 yy]# [ -e /etc/ ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

這里/etc是存在的目錄,-e用于判斷存在不存在,不判斷文件類型,所以返回值為0。


(4)使用連接符?

前面講的判斷只是單個判斷,如果要同時做多個判斷,那么就需要使用連接符了。能用的連接符包括“&&”和“||”。???

先看一下使用&&作為連接符,用法如下。

  • 判斷1 && 判斷2?

? 只有兩個判斷都為真(返回值為0),整體才為真,只要有一個為假,整體就為假。判斷1如果為假,判斷2還有必要執行嗎?沒有,因為整體已經確定為假了。判斷1為真,整體是真是假在于判斷2,所以判斷2肯定是要執行的。?

[root@rhel03 yy]# [ 1 -eq 2 ] && [ 2 -ge 3 ]
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]# 

? 這里有兩個判斷,第一個判斷是1小于等于2,這個判斷成立,第二個判斷是2大于等于3,這個 判斷不成立。使用&&作為連接符,需要兩邊的判斷都成立,整體才成立,所以整個判斷為 假,返回值為非零。?

[root@rhel03 yy]# [ 1 -le 2 ] && [ 2 -le 3 ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

? 這里有兩個判斷,第一個判斷是1小于等于2,這個判斷成立,第二個判斷是2小于等于3,這 個判斷也成立。使用&&作為連接符,需要兩邊的判斷都成立,整體才成立,所以整個判斷 為真,返回值為0。?

下面看使用||作為連接符,用法如下。

  • 判斷1 || 判斷2?

兩個判斷只要有一個為真(返回值為0),整體就為真,只有全都為假,整體才為假。

判斷1為真,整體已經確定為真,所以判斷2沒有必要執行。

判斷1為假,整體是真是假在于判斷2,所以判斷2肯定是要執行的。?

[root@rhel03 yy]# [ 1 -le 2 ] || [ 2 -ge 3 ]
[root@rhel03 yy]# echo $?
0
[root@rhel03 yy]# 

? 這里有兩個判斷,第一個判斷是1小于等于2,這個判斷成立,整體已經確定為真,所以整個判斷為真,返回值為0。?

[root@rhel03 yy]# [ 1 -ge 2 ] || [ 2 -ge 3 ]
[root@rhel03 yy]# echo $?
1
[root@rhel03 yy]#

? 這里有兩個判斷,第一個判斷是1大于等于2,第二個判斷是2大于等于3,這兩個判斷都為假,所以整個判斷為假,返回值為非零。?


if判斷語句?

? 在腳本中執行某條命令需要滿足一定的條件,如果不滿足就不能執行。此時我們就要用到判斷語句了。?

先看if判斷,if判斷的語法如下。

if 條件1 ; then

命令1

elif 條件2 ; then

命令2

else 命令3

fi?

先判斷if后面的判斷是不是成立。

如果成立,則執行命令1,然后跳到f后面,執行6后面的命令。

如果不成立,則不執行命令1,然后判斷elif后面的條件2是不是成立。

如果成立,則執行命令2,然后跳到f后面,執行f后面的命令。

如果不成立,則不執行命令2,進行下一輪的elif 判斷,以此類推。

如果所有if和elif都不成立,則執行clse中的命令3。

練習1:寫一個腳本/opt/sc1.sh,要求只有root用戶才能執行此腳本,其他用戶不能執行,命令如下。

[root@rhel03 opt]# cat scl.sh 
#/bin/bash
if [ $UID -ne 0 ]; thenecho "只有root才能執行此腳本"exit 1
fiecho "hello root"
[root@rhel03 opt]# chmod +x /opt/scl.sh 
[root@rhel03 opt]# 

腳本分析如下。

? root的uid是0,其他用戶的uid不為0。第一個判斷,如果uid不等于0,則打印警告信息“只有root才能執行此腳本”,然后exit退出腳本。?

? 如果這里不加 exit,判斷之后仍然會繼續執行echo "hello root"命令,這樣判斷就失去了意義。只有加了exit之后,如果不是root,則到此結束,不要繼續往下執行了。

如果是tom執行此腳本,則判斷成立,打印完警告信息之后,通過exit退出腳本。

如果是 root執行此腳本,則判斷不成立,直接執行f后面的命令。

使用root用戶執行此腳本的結果如下。

[root@rhel03 opt]# ./scl.sh 
hello root
[root@rhel03 opt]# 

使用tom用戶執行此腳本的結果如下。

[tom@rhel03 opt]$ ./scl.sh 
只有root才能執行此腳本
[tom@rhel03 opt]$ 

練習2:寫一個腳本 lopt/sc2.sh,運行腳本時,后面必須跟一個參數,參數是系統中的一 個文件。?

如果這個文件不存在,則顯示該文件不存在;如果存在,則顯示該文件的行數,命令如下。?

[root@rhel03 opt]# cat sc2.sh 
#!/bin/bash
if [ $# -eq 0 ] ; thenecho "腳本后面必須跟一個參數"exit 1
fi
if [ -f $1 ] ; thenwc -l $1
elseecho "$1不存在"
fi
[root@rhel03 opt]# chmod +x /opt/sc2.sh
[root@rhel03 opt]# 

腳本分析如下。

$#表示參數的個數,第一個判斷中,$#的值如果等于0,則說明腳本后面沒有跟任何參數,打印“腳本后面必須跟一個參數”,然后退出腳本。?

如果后面跟了參數,則第一個判斷不成立,然后進行下一個if判斷。

第一個參數用$1來表示,[ -f $1 ]用于判斷所跟的參數是不是存在,如果存在則執行wc -1 $1命令,如果不存在則執行else 中的命令。

運行腳本,效果如下。

[root@rhel03 opt]# ./sc2.sh 
腳本后面必須跟一個參數
[root@rhel03 opt]# 

這次運行沒有跟任何參數,則提示必須跟一個參數。

[root@rhel03 opt]# ./sc2.sh /etc/hostsxxx
/etc/hostsxxx不存在
[root@rhel03 opt]# 

這里跟了一個不存在的文件/etc/hostsxxx,腳本提示這個文件不存在。

[root@rhel03 opt]# ./sc2.sh /etc/hosts
3 /etc/hosts
[root@rhel03 opt]# 

這次腳本后面跟了一個存在的文件/etc/hosts,腳本會顯示該文件的行數,為3行。?


for循環語句?

? 有時我們需要做多次重復的操作,例如,創建100個用戶,創建一個用戶需要兩條命 令:useradd和 passwd。那么,創建100個用戶就要重復執行100次,總共執行200條命令, 此時我們就可以利用for循環簡化操作,讓系統自動幫我們重復運行即可。?

?for循環的語法如下。

for 變量 in 值‐1 值‐2 值‐3 值‐4 ; do

命令 $變量

done

? 這里首先把值-1賦值給變量,執行do和done之間的命令,所有命令執行完成之后,再把 值-2賦值給變量,執行do和done之間的命令,執行完所有命令之后,再把值-3賦值給變 量,以此類推,直到把所有的值都賦值給變量。?

看一個簡單的例子,如下所示。

[root@rhel03 opt]# for i in 1 2 3 4 ; do
> let i=$i+10
> echo $i
> done
11
12
13
14
[root@rhel03 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,第二次循環結束。


while 循環語句?

while也可以循環,while循環的語法如下。

while 判斷 ; do

命令1

命令2

done?

? 如果while后面的判斷成立,則執行do和 done之間的命令,在最后一個命令執行完成之 后,會回頭再次判斷一下while后面的判斷是不是成立。如果不成立,則跳出循環執行done后 面的命令;如果成立,則繼續執行do和 done之間的命令,就這樣循環下去。?

先看一個簡單的例子,寫一個腳本/opt/sc3.sh,命令如下。

[root@rhel03 opt]# cat sc3.sh 
#!/bin/bash
declare -i n=1
while [ $n -le 4 ] ; doecho $nlet n=$n+1
done
[root@rhel03 opt]# chmod +x sc3.sh 
[root@rhel03 opt]#

腳本分析如下。

? 這里先通過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也可以用于循環一個文件的內容,用法如下。

?while read aa ; do

命令

done < file

? 這里read后面的變量aa是可以隨意指定的,整體的意思是首先讀取file的第一行內容賦值 給aa,執行do和 done之間的命令。然后讀取file的第二行內容賦值給aa,執行do和done 之間的命令,直到讀取到file的最后一行。?

有時while需要一直循環下去(死循環),語法如下。

  • while true ; do
  • 命令
  • done

或者

  • while ((1)) ; do
  • 命令
  • done

或者

  • while : ; do
  • 命令
  • done

?

下面寫一個腳本,來實時判斷vsftpd是否啟動,如果沒有啟動,則將vsftpd啟動,命令如下。?

[root@rhel03 opt]# cat sc4.sh 
#!/bin/bash
while : ; dosystemctl is-active vsftpd &> /dev/null
if [ $? -ne 0 ]; thensystemctl start vsftpd
fisleep 1
done
[root@rhel03 opt]# chmod +x /opt/sc4.sh 
[root@rhel03 opt]# 

? 這里寫了一個 while循環,可以一直循環下去,循環中先判斷vsftpd是否啟動,如果啟動 了則返回值為0,如果沒有啟動則返回值為非零。?

? 下面開始根據返回值來進行判斷,如果$?不等于0,說明vsftpd沒有啟動,則啟動vsftpd 服務。sleep 1的意思是暫停1秒,這樣就實現了每隔1秒來判斷一次vsfilpd是否啟動。

下面開始測試這個腳本,先把腳本放在后臺運行,命令如下。

[root@rhel03 opt]# ./sc4.sh  &
[2] 4079
[root@rhel03 opt]#

測試當前vsftpd 的狀態,命令如下。

[root@rhel03 opt]# systemctl is-active vsftpd
active
[root@rhel03 opt]# 

關閉vsftpd服務之后,再次檢測vsftpd 的狀態,命令如下。

[root@rhel03 opt]# systemctl stop vsftpd
[root@rhel03 opt]# systemctl is-active vsftpd
active
[root@rhel03 opt]# 

可以看到,vsftpd 仍然是啟動的,說明我們的腳本生效了。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212739.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212739.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212739.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于Html+騰訊云播SDK開發的m3u8播放器

周末業余時間在家無事&#xff0c;學習了一下騰訊的云播放sdk&#xff0c;并制作了一個小demo&#xff08;m3u8播放器&#xff09;&#xff0c;該在線工具是基于騰訊的云播sdk開發的&#xff0c;云播sdk非常牛&#xff0c;可以支持多種播放格式。 預覽地址 m3u8player.org 源碼…

JVM進程緩存

引言 緩存在日常開發中啟動至關重要的作用&#xff0c;由于是存儲在內存中&#xff0c;數據的讀取速度是非常快的&#xff0c;能大量減少對數據庫的訪問&#xff0c;減少數據庫的壓力。我們把緩存分為兩類&#xff1a; 分布式緩存&#xff0c;例如Redis&#xff1a; 優點&…

Mybatis之簡介、使用操作(安裝、XML、SqlSession、映射的SQL語句、命名空間、作用域和生命周期)

學習的最大理由是想擺脫平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;遲一天就多一天平庸的困擾。各位小伙伴&#xff0c;如果您&#xff1a; 想系統/深入學習某技術知識點… 一個人摸索學習很難堅持&#xff0c;想組團高效學習… 想寫博客但無從下手&#xff0c;急需…

Java項目-瑞吉外賣Day4

實現文件的上傳下載&#xff1a; 前端代碼&#xff1a; 對文件的操作就是對流的操作。 上傳文件的后端代碼&#xff0c;需要注意MultipartFile的名字必須與前端相對&#xff1a; 為文件存儲位置進行動態設置&#xff0c;配置application.xml 在CommonController中設置屬性讀…

Nodejs后端+express框架

前言 基于vue3Node后臺管理項目&#xff0c;補充nodejs和express相關知識。 文章目錄 一&#xff0c;express 1.官網 Express - 基于 Node.js 平臺的 web 應用開發框架 - Express中文文檔 | Express中文網 2.安裝 npm install express --save 二、MongoDB 特點 非關…

uniapp 藍牙小程序

在 uni-app 中開發藍牙相關的小程序涉及到使用 uni-app 提供的藍牙 API。uni-app 為多端開發提供了統一的 API&#xff0c;這意味著你編寫的代碼可以在不同的平臺上運行&#xff0c;包括微信小程序。 以下是實現藍牙功能的基本步驟和代碼示例&#xff1a; 1. 開啟藍牙適配器 …

java之SpringBoot開發實用篇

MENU SpringBoot開發實用篇KF-1.熱部署KF-1-1.手動啟動熱部署KF-1-2.自動啟動熱部署KF-1-3.參與熱部署監控的文件范圍配置KF-1-4.關閉熱部署 KF-2.配置高級KF-2-1.ConfigurationPropertiesKF-2-2.寬松綁定/松散綁定KF-2-3.常用計量單位綁定KF-2-4.校驗KF-2-5.數據類型轉換 KF-3…

【頭歌系統數據庫實驗】實驗8 SQL的復雜多表查詢-2

目錄 第1關&#xff1a;基于派生表查詢每個隊員解答中超過他平均memory的user_id及題目編號problem_id 第2關&#xff1a;用ANY/ALL實現查詢2019級選手&#xff08;user_id前4位為2019&#xff09;滿足比2020級其中一個選手注冊時間早即可的選手 第3關&#xff1a;用聚集查詢…

python zblog API實現類似XMLRPC/發布文章

我發現python對Zblog的XML發布并不友好&#xff0c;雖然也有對應的模塊&#xff0c;但是遠遠沒有XPCRPC更直接方便&#xff0c;但是使用xmlRpc是直接給發布文章帶來了不小的便利&#xff0c;但是對系統也并不友好&#xff0c;但是zblog也開放了Api&#xff0c;但是干部子弟不樂…

UE小:物品拼裝功能

藍圖B1的實現步驟&#xff1a; 獲取玩家控制器和視角&#xff1a;首先獲取玩家控制器&#xff0c;然后使用Deproject Screen to World節點將屏幕上的鼠標位置轉換為世界空間中的一條射線。 射線檢測&#xff1a;使用Line Trace by Channel或Line Trace for Objects節點發射射線…

深度學習測試流程

深度學習模型測試的功能旨在驗證模型在各種情況下的性能和魯棒性。以下是深度學習模型測試的主要功能&#xff1a; 性能評估&#xff1a; 測試模型在任務目標上的整體性能&#xff0c;例如分類準確性、回歸誤差等。評估指標的選擇取決于具體的任務類型。 泛化能力&#xff1a;…

《信息技術時代》期刊雜志論文發表投稿

《信息技術時代》期刊收稿方向&#xff1a;通信工程、大數據、計算機、辦公自動化、信息或計算機教育、電子技術、系統設計、移動信息、圖情信息研究、人工智能、智能技術、信息技術與網絡安全等。 刊名&#xff1a;信息技術時代 主管主辦單位&#xff1a;深圳灣科技發展有限…

C++筆記之int、size_t、uint8_t、unsigned char*區別

C筆記之int、size_t、uint8_t、unsigned char*區別 code review! 文章目錄 C筆記之int、size_t、uint8_t、unsigned char*區別1.ChatGPT第一次查詢解釋2.ChatGPT第二次查詢解釋3.分別的使用示例 1.ChatGPT第一次查詢解釋 size_t、uint8_t 和 int 是編程中使用的不同類型&…

《微信小程序開發從入門到實戰》學習四十七

4.4 云函數 4.4.5 云函數的定時觸發 如果云函數需要定時執行&#xff0c;可以使用云函數定時觸發器。配置了定時觸發器&#xff0c;云函數會在相應時間點被自動觸發。函數返回結果不會返回調用方 在需要添加觸發器的云函數下新建文件config.json。格式如下&#xff1a; &quo…

05-詳解調用服務時負載均衡的配置及其原理

負載均衡 負載均衡的原理(通用) LoadBalanced注解用來攔截它所標記的RestTemplate發起的http請求, 底層是利用了一個名為Ribbon的組件來實現負載均衡功能(Cloud高版本已經棄用) LoadBalancerInterceptor的intercept方法會對RestTemplate的請求進行攔截 public class LoadBal…

【Linux】使用Bash和GNU Parallel并行解壓縮文件

介紹 在本教程中&#xff0c;我們將學習如何使用Bash腳本和GNU Parallel實現高效并行解壓縮多個文件。這種方法在處理大量文件時可以顯著加快提取過程。 先決條件 確保系統上已安裝以下內容&#xff1a; BashGNU Parallel 你可以使用以下命令在不同Linux系統上安裝它們&am…

【數據庫】分支與循環函數存儲過程

目錄 函數 【1】 【2】 【3】? 【4】 存儲過程 思考&分析 函數 【1】 --&#xff08;1&#xff09;定義一個根據學生姓名查詢該生學習課程的函數stu_count。 --create function stu_count(sname char(8))--學生姓名 --returns char(12)--學習課程 --begin --…

layui分頁laypage結合Flask+Jinja2實現流程

Layui2.0普通用法<!DOCTYPE html> <html> <head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>Demo</title><!-- 請勿在項目正式環境中引用該 …

uniapp iOS離線打包——運行項目到模擬器報錯?

運行項目、打包時報錯問題 記錄個人在開發過程中遇到的相關問題&#xff0c;后續有時間會不定時更新 文章目錄 運行項目、打包時報錯問題運行到模擬器報錯解決方案 打包報錯解決方案 運行到模擬器報錯 解決方案 選中項目工程 —> Build Settings 滑動底部 —> User-Defi…

Java基礎課的中下基礎課04

目錄 二十三、集合相關 23.1 集合 &#xff08;1&#xff09;集合的分支 23.2 List有序可重復集合 &#xff08;1&#xff09;ArrayList類 &#xff08;2&#xff09;泛型 &#xff08;3&#xff09;ArrayList常用方法 &#xff08;4&#xff09;Vector類 &#xff08;…