1. 程式人生 > >數值運算,if結構

數值運算,if結構

數值 等待 detail 方式 表達式 中一 影響 who 方案

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的加減乘除和求模運算結果:

  1. [root@svr5 ~]# X=1234 //定義變量X
  2. [root@svr5 ~]# expr $X + 78 //加法
  3. 1312
  4. [root@svr5 ~]# expr $X - 78 //減法
  5. 1156
  6. [root@svr5 ~]# expr $X \* 78 //乘法,操作符應添加\轉義
  7. 96252
  8. [root@svr5 ~]# expr $X / 78 //除法,僅保留整除結果
  9. 15
  10. [root@svr5 ~]# expr $X % 78 //求模
  11. 64

2)使用$[]或$(())表達式

乘法操作*無需轉義,運算符兩側可以無空格;引用變量可省略 $ 符號;計算結果替換表達式本身,可結合echo命令輸出。

同樣對於變量X=1234,分別計算與78的加減乘除和求模運算結果:

  1. [root@svr5 ~]# X=1234
  2. [root@svr5 ~]# echo $[X+78]
  3. 1312
  4. [root@svr5 ~]# echo $[X-78]
  5. 1156
  6. [root@svr5 ~]# echo $[X*78]
  7. 96252
  8. [root@svr5 ~]# echo $[X/78]
  9. 15
  10. [root@svr5 ~]# echo $[X%78]
  11. 64

3)使用let命令

expr或$[]、$(())方式只進行運算,並不會改變變量的值;而let命令可以直接對變量值做運算再保存新的值。因此變量X=1234,在執行let運算後的值會變更;另外,let運算操作並不顯示結果,但是可以結合echo命令來查看:

  1. [root@svr5 ~]# X=1234
  2. [root@svr5 ~]# let y=X+22
  3. [root@svr5 ~]# echo $y
  4. 1256
  5. [root@svr5 ~]# let X+=78 ; echo $X # X+=78(X=X+78)
  6. 1312
  7. [root@svr5 ~]# let X-=78 ; echo $X # X-=78(X=X-78)
  8. 1234
  9. [root@svr5 ~]# let X*=78 ; echo $X # X*=78(X=X*78)
  10. 96252
  11. [root@svr5 ~]# let X/=78 ; echo $X # X/=78(X=X/78)
  12. 1234
  13. [root@svr5 ~]# let X%=78 ; echo $X # X%=78(X=X%79)
  14. 64

步驟二:小數運算工具

1)bc交互式運算

先執行bc命令進入交互環境,然後再輸入需要計算的表達式。以計算小數12.34與5.678的四則運算為例,相關操作如下:

  1. [root@svr5 ~]# bc
  2. bc 1.06.95
  3. Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
  4. This is free software with ABSOLUTELY NO WARRANTY.
  5. For details type `warranty‘.
  6. 12.34+56.78 //加法
  7. 69.12
  8. 12.34-56.78 //減法
  9. -44.44
  10. 12.34*56.78 //乘法
  11. 700.66
  12. 12.34/56.78 //除法
  13. 0
  14. quit //退出交互計算器
  15. [root@svr5 ~]#

2)bc非交互式運算

將需要運算的表達式通過管道操作交給bc運算。註意,小數位的長度可采用scale=N限制,除此以外也受參與運算的數值的小數位影響。以計算小數12.34與5.678的四則運算為例,相關操作如下:

  1. [root@svr5 ~]# echo ‘scale=4;12.34+5.678‘ | bc
  2. 18.018
  3. [root@svr5 ~]# echo ‘scale=4;12.34-5.678‘ | bc
  4. 6.662
  5. [root@svr5 ~]# echo ‘scale=4;12.34*5.678‘ | bc
  6. 70.0665
  7. [root@svr5 ~]# echo ‘scale=4;12.34/5.678‘ | bc
  8. 2.1733

2 案例2:條件測試操作

2.1 問題

本案例要求參考PPT上的示例,分別練習以下條件測試操作:

  • 字符串匹配
  • 比較整數值的大小
  • 識別文件/目錄的狀態
  • 多個條件/操作的邏輯組合

2.2 步驟

實現此案例需要按照如下步驟進行。

步驟一:條件測試的基本用法

1)語法格式

使用“test 表達式”或者[ 表達式 ]都可以,表達式兩邊至少要留一個空格。

條件測試操作本身不顯示出任何信息。測試的條件是否成立主要體現在命令執行後的返回狀態(即 $?),所以可以在測試後查看變量$?的值來做出判斷,或者結合&&、||等邏輯操作顯示出結果(或作其他操作) 。

步驟二:字符串測試

1)== 比較兩個字符串是否相同

檢查當前用戶是否為root。

當root用戶執行時:

  1. [root@svr5 ~]# [ $USER == "root" ] //測試
  2. [root@svr5 ~]# echo $? //查看結果0為對,非0為錯

當普通用戶執行時:

  1. [zengye@svr5 ~]$ [ $USER == "root" ]
  2. [zengye@svr5 ~]$ echo $?

2)!= 比較兩個字符串是否不相同

檢查當前用戶,如果不是root。

當普通用戶執行時:

  1. [zengye@svr5 ~]$ [ $USER != "root" ]

當root用戶執行時:

  1. [root@svr5 ~]# [ $USER != "root" ]

3)-z 檢查變量的值是否未設置(空值)

  1. [root@svr5 ~]# var1="Tarena" ; var2=""
  2. [root@svr5 ~]# [ -z "$var1" ] && echo "空值" || echo "非空值"
  3. 非空值
  4. [root@svr5 ~]# [ -z $var2 ] && echo "空值" || echo "非空值"
  5. 空值 //變量var2已設置,但無任何值,視為空
  6. [root@svr5 ~]# [ ! -z $var1 ] //測試var1是否為非空

步驟三:整數值比較

參與比較的必須是整數(可以調用變量),比較非整數值時會出錯:

  1. [root@svr5 ~]# A=20.4
  2. [root@svr5 ~]# [ $A -gt 10 ] //不支持小數比較
  3. -bash: [: 20.4: integer expression expected

1)-eq 比較兩個數是否相等。

  1. [root@svr5 ~]# X=20 //定義一個測試變量
  2. [root@svr5 ~]# [ $X -eq 20 ] && echo "相等" || echo "不相等"
  3. 相等
  4. [root@svr5 ~]# [ $X -eq 30 ] && echo "相等" || echo "不相等"
  5. 不相等

2)-ne 比較兩個數是否不相等。

  1. [root@svr5 ~]# [ $X -ne 20 ] && echo "不等於" || echo "等於"
  2. 等於
  3. [root@svr5 ~]# [ $X -ne 30 ] && echo "不等於" || echo "等於"
  4. 不等於

3)-gt 比較前面的整數是否大於後面的整數。

  1. [root@svr5 ~]# [ $X -gt 10 ] && echo "大於" || echo "否"
  2. 大於
  3. [root@svr5 ~]# [ $X -gt 20 ] && echo "大於" || echo "否"
  4. [root@svr5 ~]# [ $X -gt 30 ] && echo "大於" || echo "否"

4)-ge 比較前面的整數是否大於或等於後面的整數。

  1. [root@svr5 ~]# [ $X -ge 10 ] && echo "大於或等於" || echo "否"
  2. 大於或等於
  3. [root@svr5 ~]# [ $X -ge 20 ] && echo "大於或等於" || echo "否"
  4. 大於或等於
  5. [root@svr5 ~]# [ $X -ge 30 ] && echo "大於或等於" || echo "否"

5)-lt 比較前面的整數是否小於後面的整數。

  1. [root@svr5 ~]# [ $X -lt 10 ] && echo "小於" || echo "否"
  2. [root@svr5 ~]# [ $X -lt 20 ] && echo "小於" || echo "否"
  3. [root@svr5 ~]# [ $X -lt 30 ] && echo "小於" || echo "否"
  4. 小於

6)-le 比較前面的整數是否小於或等於後面的整數。

  1. [root@svr5 ~]# [ $X -le 10 ] && echo "小於或等於" || echo "否"
  2. [root@svr5 ~]# [ $X -le 20 ] && echo "小於或等於" || echo "否"
  3. 小於或等於
  4. [root@svr5 ~]# [ $X -le 30 ] && echo "小於或等於" || echo "否"
  5. 小於或等於

7)提取當前登錄的用戶數,比較是否超過5。

  1. [root@svr5 ~]# who | wc -l //確認已登錄的用戶數
  2. 4
  3. [root@svr5 ~]# N=$(who | wc -l) //賦值給變量N
  4. [root@svr5 ~]# [ $N -gt 5 ] && echo "超過了" || echo "沒超過"
  5. 沒超過

上述賦值給變量N及與5比較的操作,可以簡化為如下形式:

  1. [root@svr5 ~]# [ $(who | wc -l) -gt 5 ] && echo "超過了" || echo "沒超過"
  2. 沒超過

步驟四:識別文件/目錄的狀態

1)-e 判斷對象是否存在(不管是目錄還是文件)

  1. [root@svr5 ~]# [ -e "/usr/src/" ] && echo "存在" || echo "不存在"
  2. 存在
  3. [root@svr5 ~]# [ -e "/etc/fstab" ] && echo "存在" || echo "不存在"
  4. 存在
  5. [root@svr5 ~]# [ -e "/home/nooby" ] && echo "存在" || echo "不存在"
  6. 不存在

2)-d 判斷對象是否為目錄(存在且是目錄)

  1. [root@svr5 ~]# [ -d "/usr/src/" ] && echo "是目錄" || echo "不是目錄"
  2. 是目錄
  3. [root@svr5 ~]# [ -d "/etc/fstab" ] && echo "是目錄" || echo "不是目錄"
  4. 不是目錄
  5. [root@svr5 ~]# [ -d "/home/nooby" ] && echo "是目錄" || echo "不是目錄"
  6. 不是目錄

3)-f 判斷對象是否為文件(存在且是文件)

  1. [root@svr5 ~]# [ -f "/usr/src/" ] && echo "是文件" || echo "不是文件"
  2. 不是文件
  3. [root@svr5 ~]# [ -f "/etc/fstab" ] && echo "是文件" || echo "不是文件"
  4. 是文件
  5. [root@svr5 ~]# [ -f "/home/nooby" ] && echo "是文件" || echo "不是文件"
  6. 不是文件

4)-r 判斷對象是否可讀

此測試對root用戶無效,無論文件是否設置r權限,root都可讀:

  1. [root@svr5 ~]# cp install.log /tmp/rtest.txt //復制一個文件做測試
  2. [root@svr5 ~]# chmod -r /tmp/rtest.txt //去掉所有的r權限
  3. [root@svr5 ~]# [ -r "/tmp/rtest.txt" ] && echo "可讀" || echo "不可讀"
  4. 可讀 //root測試結果仍然可讀

切換為普通用戶,再執行相同的測試,結果變為“不可讀”:

  1. [zengye@svr5 ~]$ [ -r "/tmp/rtest.txt" ] && echo "可讀" || echo "不可讀"
  2. 不可讀

普通用戶只對自己擁有r權限的文件或目錄,-r測試時結果才成立:

  1. [zengye@svr5 ~]$ ls -l .bashrc
  2. -rw-r--r-- 1 zengye zengye 124 09-24 16:44 .bashrc
  3. [zengye@svr5 ~]$ [ -r ".bashrc" ] && echo "可讀" || echo "不可讀"
  4. 可讀

5)-w 判斷對象是否可寫

此測試同樣對root用戶無效,無論文件是否設置w權限,root都可寫:

  1. [root@svr5 ~]# chmod -w /tmp/rtest.txt //去掉所有的w權限
  2. [root@svr5 ~]# ls -l /tmp/rtest.txt //確認設置結果
  3. ---------- 1 root root 33139 12-11 10:43 /tmp/rtest.txt
  4. [root@svr5 ~]# [ -w "/tmp/rtest.txt" ] && echo "可寫" || echo "不可寫"
  5. 可寫

切換為普通用戶,可以正常使用-w測試:

  1. [zengye@svr5 ~]$ ls -l /tmp/rtest.txt
  2. ---------- 1 root root 33139 12-11 10:52 /tmp/rtest.txt
  3. [zengye@svr5 ~]$ [ -w "/tmp/rtest.txt" ] && echo "可寫" || echo "不可寫"
  4. 不可寫

6)-x 判斷對象是否具有可執行權限

這個取決於文件本身、文件系統級的控制,root或普通用戶都適用:

  1. [root@svr5 ~]# chmod 644 /tmp/rtest.txt //重設權限,無x
  2. [root@svr5 ~]# ls -l /tmp/rtest.txt //確認設置結果
  3. -rw-r--r-- 1 root root 33139 12-11 10:52 /tmp/rtest.txt
  4. [root@svr5 ~]# [ -x "/tmp/rtest.txt" ] && echo "可執行" || echo "不可執行"
  5. 不可執行
  6. [root@svr5 ~]# chmod +x /tmp/rtest.txt //添加x權限
  7. [root@svr5 ~]# [ -x "/tmp/rtest.txt" ] && echo "可執行" || echo "不可執行"
  8. 可執行

步驟五:多個條件/操作的邏輯組合

1)&&,邏輯與

給定條件必須都成立,整個測試結果才為真。

檢查變量X的值是否大於10,且小於30:

  1. [root@svr5 ~]# X=20 //設置X變量的值為20
  2. [root@svr5 ~]# [ $X -gt 10 ] && [ $X -lt 30 ] && echo "YES"
  3. YES

多個條件組合時,可以使用 [[ .. ]] 界定,比如上述測試可以改為如下:

  1. [root@svr5 ~]# [[ $X -gt 10 && $X -lt 30 ]] && echo "YES"
  2. YES

2)||,邏輯或

只要其中一個條件成立,則整個測試結果為真。

檢查變量X的值是否小於10或者小於30:

  1. [root@svr5 ~]# [[ $X -lt 10 || $X -lt 30 ]] && echo "YES"
  2. YES

只要/tmp/、/var/spool/目錄中有一個可寫,則條件成立:

  1. [root@svr5 ~]# [ -w "/tmp/" ] || [ -w "/var/spool/" ] && echo "OK"
  2. OK

3 案例3:使用if選擇結構

3.1 問題

本案例要求編寫3個Shell腳本,分別實現以下目標:

  • 檢測/media/cdrom目錄,若不存在則創建
  • 檢測並判斷指定的主機是否可ping通
  • 從鍵盤讀取一個論壇積分,判斷論壇用戶等級,等級分類如下:

大於等於90 神功絕世

大於等於80,小於90 登峰造極

大於等於70,小於80 爐火純青

大於等於60,小於70 略有小成

小於60 初學乍練

3.2 方案

if單分支的語法組成:

  1. if 條件測試
  2. then 命令序列
  3. fi

if雙分支的語法組成:

  1. if 條件測試
  2. then 命令序列1
  3. else 命令序列2
  4. fi

if多分支的語法組成:

  1. if 條件測試1
  2. then 命令序列1
  3. elif 條件測試2
  4. then 命令序列2
  5. else 命令序列n
  6. fi

if多分支結構實際上相當於多層if嵌套:

  1. if 條件測試1 ; then
  2. 命令序列1
  3. else
  4. if 條件測試2 ; then
  5. 命令序列2
  6. else
  7. .. ..
  8. 命令序列n
  9. fi
  10. fi

3.3 步驟

實現此案例需要按照如下步驟進行。

步驟一:檢測/media/cdrom目錄,若不存在則創建

1)編寫腳本如下:

  1. [root@svr5 ~]# vim chkmountdir.sh
  2. #!/bin/bash
  3. MOUNT_DIR="/media/cdrom/"
  4. if [ ! -d $MOUNT_DIR ]
  5. then
  6. mkdir -p $MOUNT_DIR
  7. fi
  8. [root@svr5 ~]# chmod +x chkmountdir.sh //添加可執行權限

2)測試、驗證腳本功能

  1. [root@svr5 ~]# ls -ld /media/cdrom //本來沒有/media/cdrom目錄
  2. ls: /media/cdrom: 沒有那個文件或目錄
  3. [root@svr5 ~]# ./chkmountdir.sh //執行腳本
  4. [root@svr5 ~]# ls -ld /media/cdrom //再檢查已經有了
  5. drwxr-xr-x 2 root root 4096 12-11 15:16 /media/cdrom

有了/media/cdrom文件夾以後,再次執行上述腳本,實際上不做任何有效操作:

  1. [root@svr5 ~]# ./chk_bakdir.sh
  2. [root@svr5 ~]#

步驟二:檢測並判斷指定的主機是否可ping通

1)分析任務需求

使用ping命令檢測目標主機時,人工可直接判斷反饋結果,而腳本卻不方便。但是當ping測試成功時,執行狀態$?的值為0;而ping測試失敗時,$?的值不為0。因此在Shell腳本中可以利用這一點來判斷ping目標主機的成敗。

為了節省ping測試時間,可以只發送3個測試包(-c 3)、縮短發送測試包的間隔秒數(-i 0.2)、等待反饋的超時秒數(-W 3)。比如,檢查可ping通的主機:

  1. [root@svr5 ~]# ping -c 3 -i 0.2 -W 3 192.168.4.5
  2. PING 192.168.4.5 (192.168.4.5) 56(84) bytes of data.
  3. 64 bytes from 192.168.4.5: icmp_seq=1 ttl=64 time=0.131 ms
  4. 64 bytes from 192.168.4.5: icmp_seq=2 ttl=64 time=0.076 ms
  5. 64 bytes from 192.168.4.5: icmp_seq=3 ttl=64 time=0.073 ms
  6. --- 192.168.4.5 ping statistics ---
  7. 3 packets transmitted, 3 received, 0% packet loss, time 402ms
  8. rtt min/avg/max/mdev = 0.073/0.093/0.131/0.027 ms
  9. [root@svr5 ~]# echo $? //執行狀態表示成功
  10. 0

2)腳本編寫參考如下:

  1. [root@svr5 ~]# vim pinghost.sh
  2. #!/bin/bash
  3. ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
  4. if [ $? -eq 0 ] ; then
  5. echo "Host $1 is up."
  6. else
  7. echo "Host $1 is down."
  8. fi
  9. [root@svr5 ~]# chmod +x pinghost.sh

3)測試、驗證腳本功能

  1. [root@svr5 ~]# ./pinghost.sh 192.168.4.5
  2. Host 192.168.4.5 is up.
  3. [root@svr5 ~]# ./pinghost.sh 192.168.4.50
  4. Host 192.168.4.50 is down.

步驟三:從鍵盤讀取一個論壇積分,判斷論壇用戶等級

1)腳本編寫參考如下:

大於等於90 神功絕世

大於等於80,小於90 登峰造極

大於等於70,小於80 爐火純青

大於等於60,小於70 略有小成

大於60 初學乍練

  1. [root@svr5 ~]# vim gradediv.sh
  2. #!/bin/bash
  3. read -p "請輸入積分(0-100):" JF
  4. if [ $JF –ge 90 ] ; then
  5. echo "$JF 分,神功絕世"
  6. elif [ $JF –ge 80 ] ; then
  7. echo "$JF 分,登峰造極"
  8. elif [ $JF –ge 70 ] ; then
  9. echo "$JF 分,爐火純青"
  10. elif [ $JF –ge 60 ] ; then
  11. echo "$JF 分,略有小成"
  12. else
  13. echo "$JF 分,初學乍練"
  14. fi
  15. [root@svr5 ~]# chmod +x gradediv.sh

3)測試、驗證腳本

  1. [root@svr5 ~]# ./gradediv.sh
  2. 請輸入積分(0-100):74
  3. 74 分,爐火純青
  4. [root@svr5 ~]# ./gradediv.sh
  5. 請輸入分數(0-100):68
  6. 68 分,略有小成
  7. [root@svr5 ~]# ./gradediv.sh
  8. 請輸入分數(0-100):87
  9. 87 分,登峰造極

數值運算,if結構