三、Shell腳本編程
Shell腳本語言的運算
?算數運算
shell支持算術運算,但只支持整數,不支持小數
?Bash中的算術運算
-- + 加法運算 -- - 減法運算 -- * 乘法運算 -- / 除法運算 -- % 取模,即取余數 -- ** 乘方 ? #乘法符號在有些場景需要轉義
實現算術運算
1. let var=算術表達式
2. var=$[算術表達式]
3. var=$((算術表達式))
4. var=$(expr arg1 arg2 arg3 ...)
5. declare -i var = 數值
6. echo '算術表達式' | bc (支持浮點數)
實例:使用bc計算小數和declare -i計算
[root@localhost ~]# echo "scale=3;20/3"|bc
6.666
[root@localhost ~]# echo "scale=3;2/3"|bc
.666
[root@localhost ~]# i=20
[root@localhost ~]# j=20
[root@localhost ~]# declare -i sum=i*j
[root@localhost ~]# echo $sum
400
內建的隨機數生成器變量:
$RANDOM 取值范圍:0-32767
實例:生成0-49之間的隨機數
[root@localhost ~]# echo $[$[$RANDOM%50]+1]
40
[root@localhost ~]# echo $[$RANDOM%50]
44
[root@localhost ~]# echo $[$[$RANDOM%50]+1] #生成1~50之間的隨機數
[root@localhost ~]# echo $[RANDOM % 100 + 1]
實例:生成隨機顏色字符串
[root@localhost ~]# echo -e "\033[1;$[RANDOM%7+31]mStudy\033[0m"
Study
[root@localhost ~]# echo -e "\033[1;$[RANDOM%7+31]mStudy\033[0m"
Study
[root@localhost ~]#
增強型賦值:
+=
i+=10 <==> i=1+10
-=
i-=j <==> i=i-j
*=
/=
%=
++
i++,++i <==> i=i+1 (自增)
--
i--,--i <==> i=i-1 (自減)
格式:
let varOPERvalue
實例:自增,自減
[root@localhost ~]# let var+=1
[root@localhost ~]# echo $var
1
[root@localhost ~]# let var++
[root@localhost ~]# echo $var
2
[root@localhost ~]# let var-=1
[root@localhost ~]# echo $var
1
[root@localhost ~]# let var--
[root@localhost ~]# echo $var
0
[root@localhost ~]#
?
[root@localhost ~]# unset i j ;i=1;let j=i++;echo "i=$i,j=$j"
i=2,j=1
[root@localhost ~]# unset i j ;i=1;let j=++i;echo "i=$i,j=$j"
i=2,j=2
[root@localhost ~]#
?
# i++ 與 ++i的區別:
i++ 先賦值再運算
++i 先運算再賦值
?
實例:雞兔同籠問題
[root@localhost ~]# vim shell/chicken.sh
#!/bin/bash
HEAD=35
FOOT=94
?
RABBIT=$(((FOOT-HEAD-HEAD)/2))
CHOOK=$[35-RABBIT]
echo "兔子的數量為:"$RABBIT
echo "雞的數量為: "$CHOOK[root@localhost ~]# chmod +x shell/chicken.sh
[root@localhost ~]# shell/chicken.sh
兔子的數量為::12
雞的數量為:23
[root@localhost ~]#
?
# 在腳本中寫入變量,讓用戶在命令行寫入需要計算的數值
[root@localhost ~]# vim shell/chicken.sh
#!/bin/bash
HEAD=$1
FOOT=$2
?
RABBIT=$(((FOOT-HEAD-HEAD)/2))
CHOOK=$[35-RABBIT]
echo "兔子的數量為:"$RABBIT
echo "雞的數量為: "$CHOOK[root@ansible-salve1 ~]# ./chicken.sh 30 80
兔子的數量為:10
雞的數量為: 25
[root@ansible-salve1 ~]#
面試題:計算出所有人的年齡總和
[root@ansible-salve1 shell]# vim nianling.txt
[root@ansible-salve1 shell]# cat nianling.txt
a=20
b=18
c=22
[root@ansible-salve1 shell]# cut -d"=" -f2 nianling.txt
20
18
22
[root@ansible-salve1 shell]# cut -d"=" -f2 nianling.txt | tr '\n' + | grep -Eo ".*[0-9]" | bc
60
[root@ansible-salve1 shell]# grep -Eo "[0-9]+" nianling.txt
20
18
22
[root@ansible-salve1 shell]# grep -Eo "[0-9]+" nianling.txt | tr '\n' +| grep -Eo ".*[0-9]" | bc
60
[root@ansible-salve1 shell]#
邏輯運算
True用數字表示1,False用數字表示0
與:&
1 與 1 = 1
1 與 0 = 0
0 與 1 = 0
0 與 0 = 0
或:|
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
!1 = 0 !True=False
!0 = 1 !False=True
異或:^
#異或的兩個值,相同為假,不同為真
1 ^ 1 =0
1 ^ 0 =1
0 ^ 1 =1
0 ^ 0 =0
條件測試命令
條件測試:判斷某需求是否滿足,需要由測試機制來實現,專用的測試表達式需要由測試命令輔助完成測試過程,實現評估布爾聲明,以便在條件性環境下進行執行。
若真,則狀態碼變量$?返回0
若假,則狀態碼變量$?返回1
擴展: [ ] 與 [[ ]] 的區別
?
`區別1:
[ ]是符合POSIX標準的測試語句,兼容性強,幾乎可以運行在所有的Shell解釋器中
[[ ]]僅可運行在特定的幾個Shell解釋器中(如Bash)
?
`區別2:
> < 可以在[[ ]]中使用ASCII碼順序進行排序,而[]不支持
?
`區別3:
在[ ]中使用-a 和 -o 表示邏輯與和邏輯或,[[ ]]使用&& 和 || 表示,[[ ]]不支持-a
?
`區別4:
在[ ]中==是字符匹配,在[[ ]]中==是模式匹配
?
`區別5:
[ ]不支持正則匹配,[[ ]]支持用=~進行正則匹配
?
`區別6:
[ ]僅在部分Shell中支持用()進行分組,[[ ]]均支持
?
`區別7:
在[ ]中如果變量沒有定義,那么需要用雙引號引起來,在[[ ]]中不需要
————————————————
版權聲明:本文為CSDN博主「小白銀子」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/xiaobaiyinzi/article/details/151069124
條件測試命令及其語法
語法1:test <測試表達式> 說明:test命令和<測試表達式>之間至少有一個空格
# 在shell中,大于用 -gt 表示,小于用 -lt 表示,大于或等于用 -ge 表示,小于或等于用 -le表示 ,不相等用-ne 表示
[root@ansible-salve1 ~]# test 1 -lt 2
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]# test 2 -lt 1
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]#
語法2:[<測試表達式>] 說明:該方法和test命令的用法一樣,[]的兩邊和內容之間至少有一個空格
[root@ansible-salve1 ~]# [ 1 -gt 3 ]
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]# [ 1 -lt 3 ]
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]#
語法3:[[ <測試表達式> ]] 說明:比test和[]更新的語法格式。[[]]的邊界和內容之間至少有一個空格。[[]]中可以使用通配符等進行模式匹配
[root@ansible-salve1 ~]# [[ 1 > 3 ]]
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]# [[ 1 < 3 ]]
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]#
語法4:((<測試表達式>)) 說明:一般用于if語句里,雙小括號兩端不需要有空格,測試對象只能是整數
[root@ansible-salve1 ~]# ((1>2))
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]# ((1<2))
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]#
變量測試
語法規則:-v VAR 變量var是否被定義
示例:判斷NAME變量是否被定義
[root@ansible-salve1 shell]# [[ -v NAME ]] [root@ansible-salve1 shell]# echo $? 1 [root@ansible-salve1 shell]# NAME=1 [root@ansible-salve1 shell]# [[ -v NAME ]] [root@ansible-salve1 shell]# echo $? 0 [root@ansible-salve1 shell]#
語法規則: -R VAR 變量VAR是否被引用
示例:判斷NAME變量是否被引用
[root@ansible-salve1 shell]# NAME=10
[root@ansible-salve1 shell]# test -v NAME
[root@ansible-salve1 shell]# echo $?
0
[root@ansible-salve1 shell]# test -R NAME
[root@ansible-salve1 shell]# echo $?
1
[root@ansible-salve1 shell]#
文件測試表達式
以下是常用的文件測試操作符說明表格:
文件測試操作符對照表
操作符 | 說明 |
---|---|
-a/-e | 文件是否存在 |
-b | 文件是否存在且為塊設備文件 |
-c | 文件是否存在且為字符設備文件 |
-L/-h | 文件是否存在且為符號鏈接文件 |
-d | 文件是否存在且為目錄 |
-f | 文件是否存在且為普通文件 |
-s | 文件是否存在且大小不為0 |
-S | 文件是否存在且為套接字文件 |
-p | 文件是否存在且為管道文件 |
-u | 文件是否存在且設置了SUID權限 |
-g | 文件是否存在且設置了SGID權限 |
-r | 文件是否存在且可讀 |
-w | 文件是否存在且可寫 |
-x | 文件是否存在且可執行 |
-t fd | 文件描述符fd 是否在終端打開 |
-N | 文件自上次讀取后是否被修改 |
-O | 當前有效用戶是否為文件屬主 |
-G | 當前有效用戶是否為文件屬組 |
-ef | 文件f1 是否是文件f2 的硬鏈接(比較inode) |
-nt | 文件f1 是否比文件f2 新(根據修改時間判斷) |
-ot | 文件f1 是否比文件f2 舊(根據修改時間判斷) |
使用示例
# 檢查文件是否存在
[ -e "/path/to/file" ] && echo "文件存在"# 檢查是否為目錄
[ -d "/path/to/dir" ] && echo "是目錄"# 比較文件新舊
[ "file1" -nt "file2" ] && echo "file1比file2新"
注:操作符通常用于Shell條件測試(如test
命令或[ ]
結構中)。
測試文件
[root@ansible-salve1 ~]# test -a test.txt
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]# test -d shell
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]# touch test.txt
[root@ansible-salve1 ~]# test -w test.txt &&echo "true"
true
[root@ansible-salve1 ~]# test -r test.txt &&echo "true"
true
[root@ansible-salve1 ~]# test -x test.txt &&echo "true"
[root@ansible-salve1 ~]#
?字符串測試表達式
-n "字符串" | 字符串長度不為0時返回真(n 可理解為nonzero ) | [ ] ?或?test |
-z "字符串" | 字符串長度為0時返回真(z 可理解為zero ) | [ ] ?或?test |
"字符串1" == "字符串2" | 字符串1與字符串2完全相等時返回真(需注意空格和大小寫) | [[ ]] |
"字符串1" != "字符串2" | 字符串1與字符串2不相等時返回真 | [[ ]] |
"字符串1" > "字符串2" | 按字典序比較,字符串1的ASCII碼大于字符串2時返回真 | [[ ]] |
"字符串1" < "字符串2" | 按字典序比較,字符串1的ASCII碼小于字符串2時返回真 | [[ ]] |
"字符串1" =~ "PATTERN" | 字符串1匹配右側的擴展正則表達式時返回真(需用[[ ]] ) | [[ ]] |
測試字符串
[root@ansible-salve1 ~]# var_test='Mike'
[root@ansible-salve1 ~]# echo $var_test
Mike
[root@ansible-salve1 ~]# [[ -n var_test ]] && echo "True"
True
?
# 通配符
[root@ansible-salve1 ~]# FILE=test.log
[root@ansible-salve1 ~]# [[ "$FILE" == *.log ]] && echo "True"
True
[root@ansible-salve1 ~]# FILE=test.txt
[root@ansible-salve1 ~]# [[ "$FILE" == *.log ]] && echo "True"
[root@ansible-salve1 ~]# [[ "$FILE" != *.log ]] && echo "True"
True
[root@ansible-salve1 ~]#
?
# 擴展的正則表達式
[root@ansible-salve1 ~]# FILE=test.log
[root@ansible-salve1 ~]# [[ "$FILE" =~ \.log$ ]] && echo "True"
True
[root@ansible-salve1 ~]# N=100
[root@ansible-salve1 ~]# [[ "$N" =~ ^[0-9]+$ ]] && echo "True"
True
[root@ansible-salve1 ~]# N=A10
[root@ansible-salve1 ~]# [[ "$N" =~ ^[0-9]+$ ]] && echo "True"
[root@ansible-salve1 ~]# IP=1.2.3.4
[root@ansible-salve1 ~]# [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]#
# 使用正則表達式判斷IP是否合法
[root@ansible-salve1 ~]# IP=1.2.3.333
[root@ansible-salve1 ~]# [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]# IP=255.255.255.255
[root@ansible-salve1 ~]# [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]#
?
整數測試表達式
邏輯操作符
[root@ansible-salve1 ~]# var_test=1
[root@ansible-salve1 ~]# var_t=2
[root@ansible-salve1 ~]# [ $var_test -lt 0 -a $var_t -gt 0 ]
[root@ansible-salve1 ~]# echo $?
1
[root@ansible-salve1 ~]# [ $var_test -lt 0 -o $var_t -gt 0 ]
[root@ansible-salve1 ~]# echo $?
0
[root@ansible-salve1 ~]#
關于()與 { }
( )和 { }都可以將多個命令組合再一次,批量執行,{ } 里的內容需要與兩側用空格隔開并在命令結尾加上;
-
( )會開啟子shell,并且list中變量賦值及內部命令執行后,將不再影響后續的環境
[root@ansible-salve1 ~]# name=mage;(echo $name;name=wang;echo $name);echo $name mage wang mage ? [root@ansible-salve1 ~]# (umask 444;umask);umask 0444 0022 [root@ansible-salve1 ~]# (umask 444;touch 1.txt);umask 0022 [root@ansible-salve1 ~]# ll 1.txt --w--w--w- 1 root root 0 11月 13 22:12 1.txt [root@ansible-salve1 ~]# echo $BASHPID 1418 [root@ansible-salve1 ~]# (echo $BASHPID; sleep 3) 1501 ?
{ } 不會開啟子shell,在當前shell中運行,會影響當前shell環境
[root@ansible-salve1 ~]# name=mage;{ echo $name;name=wang;echo $name; };echo $name mage wang wang [root@ansible-salve1 ~]# { umask 444;touch 1.txt; };umask 0444 [root@ansible-salve1 ~]# umask 0444 [root@ansible-salve1 ~]# ll 1.txt --w--w--w- 1 root root 0 11月 13 22:23 1.txt [root@ansible-salve1 ~]# echo $BASHPID 1418 [root@ansible-salve1 ~]# { echo $BASHPID; sleep 3; } 1418 [root@ansible-salve1 ~]#
組合測試條件
第一種方式[ ]
[ EXPRESSION1 -a EXPRESSION2] 并且 ==> 條件1與條件2都為真,結果才為真 [ EXPRESSION1 -O EXPRESSION2] 或 ==> 條件1與條件2只要有一個為真,結果就為真 [ !EXPRESSION1 ] 取反
說明:-a 和 -o 需要使用測試命令執行,[[ ]] 不支持
[root@ansible-salve1 ~]# ll 1.txt --w--w--w- 1 root root 0 11月 13 22:23 1.txt [root@ansible-salve1 ~]# FILE=/root/1.txt [root@ansible-salve1 ~]# [ -f $FILE -a -x $FILE ] [root@ansible-salve1 ~]# echo $? 1 [root@ansible-salve1 ~]# chmod +x /root/1.txt [root@ansible-salve1 ~]# [ -f $FILE -a -x $FILE ] [root@ansible-salve1 ~]# echo $? 0 [root@ansible-salve1 ~]#
第二種方式[[ ]]
COMMAND1 && COMMAND2 #并且,短路與,代表條件性的AND THEN 如果COMMAND1 成功,將執行COMMAND2,否則,將不執行COMMAND2 ? COMMAND1 || COMMAND2 #或者,短路或,代表條件性的OR ELSE 如果COMMAND1 成功,將不執行COMMAND2,否則,將執行COMMAND2 ? ! COMMAND #非,取反 ? ? # 結論:如果&& 和 || 混合使用,&&要在前,||放在后
[root@ansible-salve1 ~]# id hehao &> /dev/null || useradd hehao # 前面執行不成功,則執行后面的語句 [root@ansible-salve1 ~]# id hehao &> /dev/null && echo "此賬戶已存在" 此賬戶已存在 [root@ansible-salve1 ~]# ? [root@ansible-salve1 ~]# IP=10.0.0.11;ping -c1 -w1 $IP &> /dev/null && echo $IP is up || echo $IP is down 10.0.0.11 is down [root@ansible-salve1 ~]# IP=192.168.80.1;ping -c1 -w1 $IP &> /dev/null && echo $IP is up || echo $IP is down 192.168.80.1 is up [root@ansible-salve1 ~]#
示例:磁盤空間的判斷
# 查看磁盤空間占用率 [root@ansible-salve1 shell]# df | grep '^/dev/nv' | grep -oE '[0-9]+%' | tr -d % 17 [root@ansible-salve1 shell]# cat fd.sh #!/bin/bash WARNING=10 SPACE_USED=`df | grep '^/dev/nv' | grep -oE '[0-9]+%' | tr -d %` ["$SPACE_USED" -gt $WARNING] && echo "磁盤空間不足,請盡快處理" | mail -s "DISK Warning" 1446528135@qq.com
使用read命令命令來接受輸入
read 是 Shell 內置命令,用來從標準輸入中讀取數據并賦值給變量。如果沒有進行重定向,默認就是從鍵盤讀取用戶輸入的數據;如果進行了重定向,那么可以從文件中讀取數據
語法結構
read [option] [variables]
?
--options表示選項
?
--variables表示用來存儲數據的變量,可以有一個,也可以有多個。
?
-- options和variables都是可選的,如果沒有提供變量名,那么讀取的數據將存放到環境變量 REPLY 中。
選項:
示例:使用read給多個變量賦值并輸出
[root@ansible-salve1 shell]# vim info.sh
#!/bin/bash
read -p "Enter some information > " name url age
echo "網站名字:$name"
echo "網址:$url"
echo "年齡:$age"
[root@ansible-salve1 shell]# chmod +x info.sh
[root@ansible-salve1 shell]# ./info.sh
Enter some information > hehao www.baidu.com 18
網站名字:hehao
網址:www.baidu.com
年齡:18
[root@ansible-salve1 shell]#
?
# 注意:必須在一行內輸入所有的值,不能換行,否則只能給第一個變量賦值,后續變量都會賦值失敗
示例:只讀取一個字符
# -n 1表示只讀取一個字符,運行腳本后,只要用戶輸入一個字符,立即就讀取結束,不等待用戶按下回車鍵
[root@ansible-salve1 shell]# vim info1.sh
#!/bin/bash
read -n 1 -p "Enter a char > " char && printf "\n"
echo "---------------------------------------------------------"
echo $char
[root@ansible-salve1 shell]# chmod +x info1.sh
[root@ansible-salve1 shell]# ./info1.sh
Enter a char > a
---------------------------------------------------------
a
示例:在指定時間內輸入密碼
#使用&&組合了多個命令,這些命令會依次執行,并且從整體上作為 if 語句的判斷條件,只要其中一個命令執行失敗(退出狀態為非 0 值),整個判斷條件就失敗了,后續的命令也就沒有必要執行
[root@ansible-salve1 shell]# vim info2.sh
#!/bin/bash
ifread -t 20 -sp "Enter password in 20 seconds(once) > " pass1 && printf "\n" && #第一次輸入密碼read -t 20 -sp "Enter password in 20 seconds(again)> " pass2 && printf "\n" && #第二次輸入密碼[ $pass1 == $pass2 ] #判斷兩次輸入的密碼是否相等
thenecho "Valid password"
elseecho "Invalid password"
fi
[root@ansible-salve1 shell]# chmod +x info2.sh
[root@ansible-salve1 shell]# ./info2.sh
Enter password in 20 seconds(once) >
Enter password in 20 seconds(again)>
Valid password
[root@ansible-salve1 shell]#
示例:利用正則表達式
[root@ansible-salve1 shell]# vim info3.sh
[root@ansible-salve1 shell]# cat info3.sh
#!/bin/bash
read -p "Are you rich? yes or no: " ANSWER
[[ $ANSWER =~ ^[Yy]|[Yy][Ee][Ss]$ ]] && echo "You Are Rich" || echo "Good Good Study,Day Day up"
[root@ansible-salve1 shell]# chmod +x info3.sh
[root@ansible-salve1 shell]# ./info3.sh
Are you rich? yes or no: y
You Are Rich
[root@ansible-salve1 shell]# ./info3.sh
Are you rich? yes or no: YeS
You Are Rich
[root@ansible-salve1 shell]#
示例:工作選項
[root@ansible-salve1 shell]# vim backup.sh
#!/bin/bash
SRC=/etc/
DEST=/data/backup_`date +%F_%H-%M-%S`
cp -a $SRC $DEST
[ $? -eq 0 ] && echo "備份成功" || echo "備份失敗"
[root@ansible-salve1 shell]# chmod +x backup.sh
[root@ansible-salve1 shell]# vim info4.sh
#!/bin/bash
cat <<EOF
請選擇:
1.備份文件
2.清理日志文件
3.軟件升級
4.軟件回滾
5.刪庫跑路
EOF
read -p "請輸入上面的數字1-5:" MENU
[ $MENU -eq 1 ] && ./backup.sh
[ $MENU -eq 2 ] && echo "清理日志文件"
[ $MENU -eq 3 ] && echo "軟件升級"
[ $MENU -eq 4 ] && echo "軟件回滾"
[ $MENU -eq 5 ] && echo "刪除跑路"
?
[root@ansible-salve1 shell]# chmod +x info4.sh
[root@ansible-salve1 shell]# ./info4.sh
請選擇:
1.備份數據庫
2.清理日志文件
3.軟件升級
4.軟件回滾
5.刪庫跑路
請輸入上面的數字1-5:1
備份成功
[root@ansible-salve1 shell]#
?