數值運算,if結構
Shell中的數值運算
1.1 問題
本案例要求熟悉Linux Shell環境的特點,主要練習以下操作:
- 使用expr、$[ ]、let等整數運算工具:定義變量X=1234,然後計算X與78的四則運算及求模結果
- 使用bc實現小數運算操作:以交互方式計算12.34與56.78的四則運算結果,另外再以非交互方式重復上述計算,最多顯示4位小數
1.2 步驟
實現此案例需要按照如下步驟進行。
步驟一:整數運算工具
1)使用expr命令
乘法操作應采用 \* 轉義,避免被作為Shell通配符;參與運算的整數值與運算操作符之間需要以空格分開,引用變量時必須加$符號。
首先定義變量X=1234,然後分別計算與78的加減乘除和求模運算結果:
- [root@svr5 ~]# X=1234 //定義變量X
- [root@svr5 ~]# expr $X + 78 //加法
- 1312
- [root@svr5 ~]# expr $X - 78 //減法
- 1156
- [root@svr5 ~]# expr $X \* 78 //乘法,操作符應添加\轉義
- 96252
- [root@svr5 ~]# expr $X / 78 //除法,僅保留整除結果
- 15
- [root@svr5 ~]# expr $X % 78 //求模
- 64
2)使用$[]或$(())表達式
乘法操作*無需轉義,運算符兩側可以無空格;引用變量可省略 $ 符號;計算結果替換表達式本身,可結合echo命令輸出。
同樣對於變量X=1234,分別計算與78的加減乘除和求模運算結果:
- [root@svr5 ~]# X=1234
- [root@svr5 ~]# echo $[X+78]
- 1312
- [root@svr5 ~]# echo $[X-78]
- 1156
- [root@svr5 ~]# echo $[X*78]
- 96252
- [root@svr5 ~]# echo $[X/78]
- 15
- [root@svr5 ~]# echo $[X%78]
- 64
3)使用let命令
expr或$[]、$(())方式只進行運算,並不會改變變量的值;而let命令可以直接對變量值做運算再保存新的值。因此變量X=1234,在執行let運算後的值會變更;另外,let運算操作並不顯示結果,但是可以結合echo命令來查看:
- [root@svr5 ~]# X=1234
- [root@svr5 ~]# let y=X+22
- [root@svr5 ~]# echo $y
- 1256
- [root@svr5 ~]# let X+=78 ; echo $X # X+=78(X=X+78)
- 1312
- [root@svr5 ~]# let X-=78 ; echo $X # X-=78(X=X-78)
- 1234
- [root@svr5 ~]# let X*=78 ; echo $X # X*=78(X=X*78)
- 96252
- [root@svr5 ~]# let X/=78 ; echo $X # X/=78(X=X/78)
- 1234
- [root@svr5 ~]# let X%=78 ; echo $X # X%=78(X=X%79)
- 64
步驟二:小數運算工具
1)bc交互式運算
先執行bc命令進入交互環境,然後再輸入需要計算的表達式。以計算小數12.34與5.678的四則運算為例,相關操作如下:
- [root@svr5 ~]# bc
- bc 1.06.95
- Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
- This is free software with ABSOLUTELY NO WARRANTY.
- For details type `warranty‘.
- 12.34+56.78 //加法
- 69.12
- 12.34-56.78 //減法
- -44.44
- 12.34*56.78 //乘法
- 700.66
- 12.34/56.78 //除法
- 0
- quit //退出交互計算器
- [root@svr5 ~]#
2)bc非交互式運算
將需要運算的表達式通過管道操作交給bc運算。註意,小數位的長度可采用scale=N限制,除此以外也受參與運算的數值的小數位影響。以計算小數12.34與5.678的四則運算為例,相關操作如下:
- [root@svr5 ~]# echo ‘scale=4;12.34+5.678‘ | bc
- 18.018
- [root@svr5 ~]# echo ‘scale=4;12.34-5.678‘ | bc
- 6.662
- [root@svr5 ~]# echo ‘scale=4;12.34*5.678‘ | bc
- 70.0665
- [root@svr5 ~]# echo ‘scale=4;12.34/5.678‘ | bc
- 2.1733
2 案例2:條件測試操作
2.1 問題
本案例要求參考PPT上的示例,分別練習以下條件測試操作:
- 字符串匹配
- 比較整數值的大小
- 識別文件/目錄的狀態
- 多個條件/操作的邏輯組合
2.2 步驟
實現此案例需要按照如下步驟進行。
步驟一:條件測試的基本用法
1)語法格式
使用“test 表達式”或者[ 表達式 ]都可以,表達式兩邊至少要留一個空格。
條件測試操作本身不顯示出任何信息。測試的條件是否成立主要體現在命令執行後的返回狀態(即 $?),所以可以在測試後查看變量$?的值來做出判斷,或者結合&&、||等邏輯操作顯示出結果(或作其他操作) 。
步驟二:字符串測試
1)== 比較兩個字符串是否相同
檢查當前用戶是否為root。
當root用戶執行時:
- [root@svr5 ~]# [ $USER == "root" ] //測試
- [root@svr5 ~]# echo $? //查看結果0為對,非0為錯
當普通用戶執行時:
- [zengye@svr5 ~]$ [ $USER == "root" ]
- [zengye@svr5 ~]$ echo $?
2)!= 比較兩個字符串是否不相同
檢查當前用戶,如果不是root。
當普通用戶執行時:
- [zengye@svr5 ~]$ [ $USER != "root" ]
當root用戶執行時:
- [root@svr5 ~]# [ $USER != "root" ]
3)-z 檢查變量的值是否未設置(空值)
- [root@svr5 ~]# var1="Tarena" ; var2=""
- [root@svr5 ~]# [ -z "$var1" ] && echo "空值" || echo "非空值"
- 非空值
- [root@svr5 ~]# [ -z $var2 ] && echo "空值" || echo "非空值"
- 空值 //變量var2已設置,但無任何值,視為空
- [root@svr5 ~]# [ ! -z $var1 ] //測試var1是否為非空
步驟三:整數值比較
參與比較的必須是整數(可以調用變量),比較非整數值時會出錯:
- [root@svr5 ~]# A=20.4
- [root@svr5 ~]# [ $A -gt 10 ] //不支持小數比較
- -bash: [: 20.4: integer expression expected
1)-eq 比較兩個數是否相等。
- [root@svr5 ~]# X=20 //定義一個測試變量
- [root@svr5 ~]# [ $X -eq 20 ] && echo "相等" || echo "不相等"
- 相等
- [root@svr5 ~]# [ $X -eq 30 ] && echo "相等" || echo "不相等"
- 不相等
2)-ne 比較兩個數是否不相等。
- [root@svr5 ~]# [ $X -ne 20 ] && echo "不等於" || echo "等於"
- 等於
- [root@svr5 ~]# [ $X -ne 30 ] && echo "不等於" || echo "等於"
- 不等於
3)-gt 比較前面的整數是否大於後面的整數。
- [root@svr5 ~]# [ $X -gt 10 ] && echo "大於" || echo "否"
- 大於
- [root@svr5 ~]# [ $X -gt 20 ] && echo "大於" || echo "否"
- 否
- [root@svr5 ~]# [ $X -gt 30 ] && echo "大於" || echo "否"
- 否
4)-ge 比較前面的整數是否大於或等於後面的整數。
- [root@svr5 ~]# [ $X -ge 10 ] && echo "大於或等於" || echo "否"
- 大於或等於
- [root@svr5 ~]# [ $X -ge 20 ] && echo "大於或等於" || echo "否"
- 大於或等於
- [root@svr5 ~]# [ $X -ge 30 ] && echo "大於或等於" || echo "否"
- 否
5)-lt 比較前面的整數是否小於後面的整數。
- [root@svr5 ~]# [ $X -lt 10 ] && echo "小於" || echo "否"
- 否
- [root@svr5 ~]# [ $X -lt 20 ] && echo "小於" || echo "否"
- 否
- [root@svr5 ~]# [ $X -lt 30 ] && echo "小於" || echo "否"
- 小於
6)-le 比較前面的整數是否小於或等於後面的整數。
- [root@svr5 ~]# [ $X -le 10 ] && echo "小於或等於" || echo "否"
- 否
- [root@svr5 ~]# [ $X -le 20 ] && echo "小於或等於" || echo "否"
- 小於或等於
- [root@svr5 ~]# [ $X -le 30 ] && echo "小於或等於" || echo "否"
- 小於或等於
7)提取當前登錄的用戶數,比較是否超過5。
- [root@svr5 ~]# who | wc -l //確認已登錄的用戶數
- 4
- [root@svr5 ~]# N=$(who | wc -l) //賦值給變量N
- [root@svr5 ~]# [ $N -gt 5 ] && echo "超過了" || echo "沒超過"
- 沒超過
上述賦值給變量N及與5比較的操作,可以簡化為如下形式:
- [root@svr5 ~]# [ $(who | wc -l) -gt 5 ] && echo "超過了" || echo "沒超過"
- 沒超過
步驟四:識別文件/目錄的狀態
1)-e 判斷對象是否存在(不管是目錄還是文件)
- [root@svr5 ~]# [ -e "/usr/src/" ] && echo "存在" || echo "不存在"
- 存在
- [root@svr5 ~]# [ -e "/etc/fstab" ] && echo "存在" || echo "不存在"
- 存在
- [root@svr5 ~]# [ -e "/home/nooby" ] && echo "存在" || echo "不存在"
- 不存在
2)-d 判斷對象是否為目錄(存在且是目錄)
- [root@svr5 ~]# [ -d "/usr/src/" ] && echo "是目錄" || echo "不是目錄"
- 是目錄
- [root@svr5 ~]# [ -d "/etc/fstab" ] && echo "是目錄" || echo "不是目錄"
- 不是目錄
- [root@svr5 ~]# [ -d "/home/nooby" ] && echo "是目錄" || echo "不是目錄"
- 不是目錄
3)-f 判斷對象是否為文件(存在且是文件)
- [root@svr5 ~]# [ -f "/usr/src/" ] && echo "是文件" || echo "不是文件"
- 不是文件
- [root@svr5 ~]# [ -f "/etc/fstab" ] && echo "是文件" || echo "不是文件"
- 是文件
- [root@svr5 ~]# [ -f "/home/nooby" ] && echo "是文件" || echo "不是文件"
- 不是文件
4)-r 判斷對象是否可讀
此測試對root用戶無效,無論文件是否設置r權限,root都可讀:
- [root@svr5 ~]# cp install.log /tmp/rtest.txt //復制一個文件做測試
- [root@svr5 ~]# chmod -r /tmp/rtest.txt //去掉所有的r權限
- [root@svr5 ~]# [ -r "/tmp/rtest.txt" ] && echo "可讀" || echo "不可讀"
- 可讀 //root測試結果仍然可讀
切換為普通用戶,再執行相同的測試,結果變為“不可讀”:
- [zengye@svr5 ~]$ [ -r "/tmp/rtest.txt" ] && echo "可讀" || echo "不可讀"
- 不可讀
普通用戶只對自己擁有r權限的文件或目錄,-r測試時結果才成立:
- [zengye@svr5 ~]$ ls -l .bashrc
- -rw-r--r-- 1 zengye zengye 124 09-24 16:44 .bashrc
- [zengye@svr5 ~]$ [ -r ".bashrc" ] && echo "可讀" || echo "不可讀"
- 可讀
5)-w 判斷對象是否可寫
此測試同樣對root用戶無效,無論文件是否設置w權限,root都可寫:
- [root@svr5 ~]# chmod -w /tmp/rtest.txt //去掉所有的w權限
- [root@svr5 ~]# ls -l /tmp/rtest.txt //確認設置結果
- ---------- 1 root root 33139 12-11 10:43 /tmp/rtest.txt
- [root@svr5 ~]# [ -w "/tmp/rtest.txt" ] && echo "可寫" || echo "不可寫"
- 可寫
切換為普通用戶,可以正常使用-w測試:
- [zengye@svr5 ~]$ ls -l /tmp/rtest.txt
- ---------- 1 root root 33139 12-11 10:52 /tmp/rtest.txt
- [zengye@svr5 ~]$ [ -w "/tmp/rtest.txt" ] && echo "可寫" || echo "不可寫"
- 不可寫
6)-x 判斷對象是否具有可執行權限
這個取決於文件本身、文件系統級的控制,root或普通用戶都適用:
- [root@svr5 ~]# chmod 644 /tmp/rtest.txt //重設權限,無x
- [root@svr5 ~]# ls -l /tmp/rtest.txt //確認設置結果
- -rw-r--r-- 1 root root 33139 12-11 10:52 /tmp/rtest.txt
- [root@svr5 ~]# [ -x "/tmp/rtest.txt" ] && echo "可執行" || echo "不可執行"
- 不可執行
- [root@svr5 ~]# chmod +x /tmp/rtest.txt //添加x權限
- [root@svr5 ~]# [ -x "/tmp/rtest.txt" ] && echo "可執行" || echo "不可執行"
- 可執行
步驟五:多個條件/操作的邏輯組合
1)&&,邏輯與
給定條件必須都成立,整個測試結果才為真。
檢查變量X的值是否大於10,且小於30:
- [root@svr5 ~]# X=20 //設置X變量的值為20
- [root@svr5 ~]# [ $X -gt 10 ] && [ $X -lt 30 ] && echo "YES"
- YES
多個條件組合時,可以使用 [[ .. ]] 界定,比如上述測試可以改為如下:
- [root@svr5 ~]# [[ $X -gt 10 && $X -lt 30 ]] && echo "YES"
- YES
2)||,邏輯或
只要其中一個條件成立,則整個測試結果為真。
檢查變量X的值是否小於10或者小於30:
- [root@svr5 ~]# [[ $X -lt 10 || $X -lt 30 ]] && echo "YES"
- YES
只要/tmp/、/var/spool/目錄中有一個可寫,則條件成立:
- [root@svr5 ~]# [ -w "/tmp/" ] || [ -w "/var/spool/" ] && echo "OK"
- OK
3 案例3:使用if選擇結構
3.1 問題
本案例要求編寫3個Shell腳本,分別實現以下目標:
- 檢測/media/cdrom目錄,若不存在則創建
- 檢測並判斷指定的主機是否可ping通
- 從鍵盤讀取一個論壇積分,判斷論壇用戶等級,等級分類如下:
大於等於90 神功絕世
大於等於80,小於90 登峰造極
大於等於70,小於80 爐火純青
大於等於60,小於70 略有小成
小於60 初學乍練
3.2 方案
if單分支的語法組成:
- if 條件測試
- then 命令序列
- fi
if雙分支的語法組成:
- if 條件測試
- then 命令序列1
- else 命令序列2
- fi
if多分支的語法組成:
- if 條件測試1
- then 命令序列1
- elif 條件測試2
- then 命令序列2
- else 命令序列n
- fi
if多分支結構實際上相當於多層if嵌套:
- if 條件測試1 ; then
- 命令序列1
- else
- if 條件測試2 ; then
- 命令序列2
- else
- .. ..
- 命令序列n
- fi
- fi
3.3 步驟
實現此案例需要按照如下步驟進行。
步驟一:檢測/media/cdrom目錄,若不存在則創建
1)編寫腳本如下:
- [root@svr5 ~]# vim chkmountdir.sh
- #!/bin/bash
- MOUNT_DIR="/media/cdrom/"
- if [ ! -d $MOUNT_DIR ]
- then
- mkdir -p $MOUNT_DIR
- fi
- [root@svr5 ~]# chmod +x chkmountdir.sh //添加可執行權限
2)測試、驗證腳本功能
- [root@svr5 ~]# ls -ld /media/cdrom //本來沒有/media/cdrom目錄
- ls: /media/cdrom: 沒有那個文件或目錄
- [root@svr5 ~]# ./chkmountdir.sh //執行腳本
- [root@svr5 ~]# ls -ld /media/cdrom //再檢查已經有了
- drwxr-xr-x 2 root root 4096 12-11 15:16 /media/cdrom
有了/media/cdrom文件夾以後,再次執行上述腳本,實際上不做任何有效操作:
- [root@svr5 ~]# ./chk_bakdir.sh
- [root@svr5 ~]#
步驟二:檢測並判斷指定的主機是否可ping通
1)分析任務需求
使用ping命令檢測目標主機時,人工可直接判斷反饋結果,而腳本卻不方便。但是當ping測試成功時,執行狀態$?的值為0;而ping測試失敗時,$?的值不為0。因此在Shell腳本中可以利用這一點來判斷ping目標主機的成敗。
為了節省ping測試時間,可以只發送3個測試包(-c 3)、縮短發送測試包的間隔秒數(-i 0.2)、等待反饋的超時秒數(-W 3)。比如,檢查可ping通的主機:
- [root@svr5 ~]# ping -c 3 -i 0.2 -W 3 192.168.4.5
- PING 192.168.4.5 (192.168.4.5) 56(84) bytes of data.
- 64 bytes from 192.168.4.5: icmp_seq=1 ttl=64 time=0.131 ms
- 64 bytes from 192.168.4.5: icmp_seq=2 ttl=64 time=0.076 ms
- 64 bytes from 192.168.4.5: icmp_seq=3 ttl=64 time=0.073 ms
- --- 192.168.4.5 ping statistics ---
- 3 packets transmitted, 3 received, 0% packet loss, time 402ms
- rtt min/avg/max/mdev = 0.073/0.093/0.131/0.027 ms
- [root@svr5 ~]# echo $? //執行狀態表示成功
- 0
2)腳本編寫參考如下:
- [root@svr5 ~]# vim pinghost.sh
- #!/bin/bash
- ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "Host $1 is up."
- else
- echo "Host $1 is down."
- fi
- [root@svr5 ~]# chmod +x pinghost.sh
3)測試、驗證腳本功能
- [root@svr5 ~]# ./pinghost.sh 192.168.4.5
- Host 192.168.4.5 is up.
- [root@svr5 ~]# ./pinghost.sh 192.168.4.50
- Host 192.168.4.50 is down.
步驟三:從鍵盤讀取一個論壇積分,判斷論壇用戶等級
1)腳本編寫參考如下:
大於等於90 神功絕世
大於等於80,小於90 登峰造極
大於等於70,小於80 爐火純青
大於等於60,小於70 略有小成
大於60 初學乍練
- [root@svr5 ~]# vim gradediv.sh
- #!/bin/bash
- read -p "請輸入積分(0-100):" JF
- if [ $JF –ge 90 ] ; then
- echo "$JF 分,神功絕世"
- elif [ $JF –ge 80 ] ; then
- echo "$JF 分,登峰造極"
- elif [ $JF –ge 70 ] ; then
- echo "$JF 分,爐火純青"
- elif [ $JF –ge 60 ] ; then
- echo "$JF 分,略有小成"
- else
- echo "$JF 分,初學乍練"
- fi
- [root@svr5 ~]# chmod +x gradediv.sh
3)測試、驗證腳本
- [root@svr5 ~]# ./gradediv.sh
- 請輸入積分(0-100):74
- 74 分,爐火純青
- [root@svr5 ~]# ./gradediv.sh
- 請輸入分數(0-100):68
- 68 分,略有小成
- [root@svr5 ~]# ./gradediv.sh
- 請輸入分數(0-100):87
- 87 分,登峰造極
數值運算,if結構