天天看點

Linux-Shell腳本程式設計(四)

作者:不寐旋律

續接上回。。。

4 流程控制

4.1 條件選擇

4.1.1 條件判斷分紹

4.1.1.1 單分支條件

Linux-Shell腳本程式設計(四)

4.1.1.2 多分支條件

Linux-Shell腳本程式設計(四)
Linux-Shell腳本程式設計(四)

4.1.2 選擇執行 if 語句

格式:

if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else 
COMMANDS; ] fi           

單分支

if 判斷條件;then
   條件為真的分支代碼
fi           

雙分支

if 判斷條件; then
 條件為真的分支代碼
else
 條件為假的分支代碼
fi           

多分支

if  判斷條件1; then
  條件1為真的分支代碼
elif  判斷條件2; then
  條件2為真的分支代碼
elif  判斷條件3; then
  條件3為真的分支代碼
...
else
  以上條件都為假的分支代碼
fi           

說明:

  • 多個條件時,逐個條件進行判斷,第一次遇為“真”條件時,執行其分支,而後結束整個if語句
  • if 語句可嵌套

案例:

#根據指令的退出狀态來執行指令
if ping -c1 -W2 station1 &> /dev/null; then     
 echo 'station1 is UP'  
elif  grep -q 'station1' ~/maintenance.txt;  then     
 echo 'station1 is undergoing maintenance'
else   
 echo 'station1 is unexpectedly DOWN!'     
 exit 1
fi           

腳本案例:身體品質指數

[root@nginx ~]#cat if_bmi.sh
 #!/bin/bash
 read -p "請輸入身高(m為機關): " HIGH
if [[ ! "$HIGH" =~ ^[0-2](\.[0-9]{,2})?$ ]];then 
 echo "輸入錯誤的身高"
 exit 1
fi
read -p "請輸入體重(kg為機關): " WEIGHT
if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then echo "輸入錯誤的體重"; exit 1; fi
BMI=`echo $WEIGHT/$HIGH^2|bc`
if [ $BMI  -le 18 ] ;then
 echo "太瘦了,多吃點"
elif [ $BMI  -lt 24 ] ;then
 echo "身材很棒!"
else
 echo "太胖了,注意節食,加強運動"
fi           

4.1.3 條件判斷 case 語句

格式:

case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac           
case 變量引用 in
PAT1)
 分支1
 ;;
PAT2)
 分支2
 ;;
...
*)
 預設分支
 ;;
esac           

case支援glob風格的通配符:

*  任意長度任意字元
?  任意單個字元
[]  指定範圍内的任意單個字元
|    或者,如: a|b           

案例:

[root@nginx ~]#cat case_yesorno.sh
#!/bin/bash
read -p "Do you agree(yes/no)? " INPUT
INPUT=`echo $INPUT | tr 'A-Z' 'a-z'`
 case $INPUT in
y|yes)
      echo "You input is YES"
     ;;
n|no)
      echo "You input is NO"
     ;;
*)
      echo "Input fales,please input yes or no!"
esac           

案例:

[root@nginx ~]#cat case_yesorno2.sh
 #!/bin/bash
 read -p "Do you agree(yes/no)? " INPUT
case $INPUT in
[yY]|[Yy][Ee][Ss])
    echo "You input is YES"
   ;;
[Nn]|[Nn][Oo])
    echo "You input is NO"
   ;;
*)
    echo "Input fales,please input yes or no!"                                   
                             
esac           

案例:檔案字尾處理

[root@nginx ~]#cat suffix.sh
 #!/bin/bash
 read -p "please input a file: " FILE
SUFFIX=`echo $FILE | grep -Eo "[^.]+#34;`
case $SUFFIX in
gz)
    echo gzip
    ;;
bz2)
    echo bzip2
   ;;
xz)
    echo xz
   ;;
Z)
    echo compress
   ;;
zip)
    echo zip
   ;;
*)
    echo other
   ;;
esac              

案例:運維菜單

[root@nginx ~]#cat work_menu.sh
 #!/bin/bash
 echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
請選擇:
1)備份資料庫
2)清理日志
3)軟體更新
4)軟體復原
5)删庫跑路
EOF
echo -en '\E[0m'
read -p  "請輸入上面數字1-5: " MENU
case $MENU in
1)
   echo "執行備份資料庫"
   #./backup.sh
   ;;
2)
    echo "清理日志"
   ;;
3)
    echo "軟體更新"
   ;;
4)
    echo "軟體復原"
   ;;
5)
    echo "删庫跑路"
   ;;
*)
    echo "INPUT FALSE!"
esac           

4.2 循環

4.2.1 循環執行介紹

Linux-Shell腳本程式設計(四)

将某代碼段重複運作多次,通常有進入循環的條件和退出循環的條件

重複運作次數

  • 循環次數事先已知
  • 循環次數事先未知

常見的循環的指令:for, while, until

Linux-Shell腳本程式設計(四)

4.2.2 循環 for

[root@nginx ~]# help for
for: for 名稱 [in 詞語 ... ] ; do 指令; done
    為清單中的每個成員執行指令。
    
    `for' 循環為清單中的每個成員執行一系列的指令。如果沒有
    `in WORDS ...;'則假定使用 `in "$@"'。對于 WORDS 中的每
     個元素,NAME 被設定為該元素,并且執行 COMMANDS 指令。
    
    退出狀态:
    傳回最後執行的指令的狀态。
for ((: for (( 表達式1; 表達式2; 表達式3 )); do 指令; done
    算術 for 循環。
    
    等價于
    	(( EXP1 ))
    	while (( EXP2 )); do
    		COMMANDS
    		(( EXP3 ))
    	done
    EXP1、EXP2 和 EXP3都是算術表達式。如果省略任何表達式,
    則等同于使用了估值為1的表達式。
    
    退出狀态:
    傳回最後執行的指令的狀态。
[root@nginx ~]#            

格式1:

for NAME [in WORDS ... ] ; do COMMANDS; done
#方式1
for 變量名  in 清單;do
 循環體
done
#方式2
for 變量名  in 清單
do
 循環體
done           

執行機制:

  • 依次将清單中的元素指派給“變量名”; 每次指派後即執行一次循環體; 直到清單中的元素耗盡,循環結束
  • 如果省略 [in WORDS ... ] ,此時使用位置參量

for 循環清單生成方式:

  • 直接給出清單
  • 整數清單:
{start..end}
$(seq [start [step]] end)           
  • 傳回清單的指令:
$(COMMAND)           
  • 使用glob,如:*.sh
  • 變量引用,如:$@,$*,$#

案例:計算1+2+3+...+100 的結果

[root@nginx ~]# sum=0;for i in {1..100};do let sum+=i;done ;echo sum=$sum
sum=5050
[root@nginx ~]#
[root@nginx ~]#seq -s+ 100|bc5050
5050
[root@nginx ~]#echo {1..100}|tr ' ' +|bc
5050
[root@nginx ~]#seq 100|paste -sd +|bc
5050           

案例:100以内的奇數之和

[root@nginx ~]#sum=0;for i in {1..100..2};do let sum+=i;done;echo sum=$sum
sum=2500
[root@nginx ~]#seq -s+ 1 2 100| bc
2500
[root@nginx ~]#echo {1..100..2}|tr ' ' + | bc
2500           

案例:

[root@nginx ~]#cat for_sum.sh
 #!/bin/bash
 sum=0
for i in $* ; do
   let sum+=i
done
echo sum=$sum
 
[root@nginx ~]#sh for_sum.sh 1 2 3 4 5 6
sum=21           

案例:批量建立使用者和并設定随機密碼

[root@nginx ~]#cat user_for.sh
 #!/bin/bash
 for i in {1..10};do
   useradd user$i
    PASS=`cat /dev/urandom | tr -dc '[:alnum:]' |head -c12`
    echo $PASS | passwd --stdin user$i &> /dev/null 
    echo user$i:$PASS >> /tmp/user.log
    echo "user$i is created"
done           

案例:九九乘法表

[root@nginx ~]#cat 9x9_for.sh
#!/bin/bash
 for i in {1..9};do
    for j in `seq $i`;do
        echo -e "${j}x${i}=$[j*i]\t\c"
    done
    echo
done           

案例:将指定目錄下的檔案所有檔案的字尾改名為 bak 字尾

[root@nginx ~]#cat for_rename.sh
 #!/bin/bash
 DIR=/tmp/test
cd $DIR
for FILE in * ;do
 PRE=`echo $FILE|grep -Eo ".*\."`
    mv $FILE ${PRE}bak
#   PRE=`echo $FILE|rev|cut -d. -f 2-|rev`
#   PRE=`echo $FILE | sed -nr 's/(.*)\.([^.]+)$/\1/p'
#   SUFFIX=`echo $FILE | sed -nr 's/(.*)\.([^.]+)$/\2/p'`
#   mv $FILE $PRE.bak
done           

案例:要求将目錄YYYY-MM-DD/中所有檔案,移動到YYYY-MM/DD/下

#1.建立YYYY-MM-DD格式的目錄,目前日期一年前365天到目前共365個目錄,裡面有10個檔案.log字尾的
檔案
[root@nginx ~]#cat for_dir.sh 
#!/bin/bash
for i in {1..365};do 
  DIR=`date -d "-$i day" +%F`
  mkdir -pv /tmp/test/$DIR
cd /tmp/test/$DIR
for n in {1..10};do
   touch $RANDOM.log
 done
done

#2.将上面的目錄移動到YYYY-MM/DD/下  
#!/bin/bash
#
DIR=/tmp/test
cd $DIR
for subdir in * ;do 
 YYYY_MM=`echo $subdir |cut -d"-" -f1,2`
 DD=`echo $subdir |cut -d"-" -f3`
 [ -d $YYYY_MM/$DD ] || mkdir -p $YYYY_MM/$DD &> /dev/null
 mv $subdir/*   $YYYY_MM/$DD
done
rm -rf $DIR/*-*-*           

格式2

雙小括号方法,即((…))格式,也可以用于算術運算,雙小括号方法也可以使bash Shell實作C語言風格的變量操作 : I=10;((I++))

Linux-Shell腳本程式設計(四)
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done

for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式))
do
 循環體
done           

說明:

  • 控制變量初始化:僅在運作到循環代碼段時執行一次
  • 控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而後再做條件判斷

案例:

#!/bin/bash
for((sum=0,i=1;i<=100;sum+=i,i++));do                       
        true
done
echo $sum           

案例:九九乘法表

#!/bin/bash
#文法1實作
for i in {1..9};do
    for j in `seq $i`;do
        echo -e "${j}x$i=$((j*i))\t\c"
    done
    echo
done

#文法2實作
for((i=1;i<10;i++));do
    for((j=1;j<=i;j++));do
        echo -e "${j}x$i=$((j*i))\t\c"
    done
    echo
done           

案例:等腰三角形

[root@nginx ~]#cat for_triangle.sh
#!/bin/bash

read -p "請輸入三角形的行數: " line
for((i=1;i<=line;i++));do
    for((k=0;k<=line-i;k++));do
        echo -e ' \c'
    done
    for((j=1;j<=2*i-1;j++));do
        echo -e '*\c'
    done
    echo
done
[root@nginx ~]#bash for_triangle.sh
請輸入三角形的行數: 10
          *
         ***
        *****
       *******
      *********
     ***********
    *************
   ***************
  *****************
 *******************           

案例:生成進度

[root@nginx ~]# for ((i = 0; i <= 100; ++i)); do printf "\e[4D%3d%%" $i;sleep 0.1s; done
100%
 [root@nginx ~]#           

4.2.3 循環 while

格式:

while COMMANDS; do COMMANDS; done

while CONDITION; do
 循環體
done           

說明:

CONDITION:循環控制條件;進入循環之前,先做一次判斷;每一次循環之後會再次做判斷;條件為“true”,則執行一次循環;直到條件測試狀态為“false”終止循環,是以:CONDTION一般應該有循環控制變量;而此變量的值會在循環體不斷地被修正

  • 進入條件:CONDITION為true
  • 退出條件:CONDITION為false

無限循環

while true; do
  循環體
done
while  :  ; do
  循環體
done           

案例:

[root@nginx ~]#sum=0;i=1;while ((i<=100));do let sum+=i;let i++;done;echo $sum
5050           

4.2.4 循環 until

格式:

until COMMANDS; do COMMANDS; done
until CONDITION; do
 循環體
done           

說明:

進入條件: CONDITION 為false

退出條件: CONDITION 為true

案例:

[root@nginx ~]#sum=0;i=1;until ((i>100));do let sum+=i;let i++;done;echo $sum
5050           

無限循環

until false; do
 循環體
Done           

4.2.4 循環控制語句 continue

continue [N]:提前結束第N層的本輪循環,而直接進入下一輪判斷;最内層為第1層

格式:

while CONDITION1; do
 CMD1
 ...
 if CONDITION2; then
 continue
 fi
 CMDn
 ...
done           

案例:

[root@nginx ~]#cat continue_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
 for((j=0;j<10;j++));do
   [ $j -eq 5 ] && continue 2
   echo $j
 done
 echo ---------------------------
done           

4.2.5 循環控制語句 break

break [N]:提前結束第N層整個循環,最内層為第1層

格式:

while CONDITION1; do
 CMD1
 ...
 if CONDITION2; then
 break
 fi
 CMDn
 ...
done           

案例:

[root@nginx ~]#cat break_for.sh
 #!/bin/bash
 for ((i=0;i<10;i++));do
 for((j=0;j<10;j++));do
   [ $j -eq 5 ] && break 
   echo $j
 done
 echo ---------------------------
done           
[root@nginx ~]#cat break_for.sh
#!/bin/bash

for ((i=0;i<10;i++));do
 for((j=0;j<10;j++));do
   [ $j -eq 5 ] && break 2
   echo $j
 done
 echo ---------------------------
done
[root@nginx ~]#bash break_for.sh
0
1
2
3
4           
[root@nginx ~]#cat guess.sh
#!/bin/bash
NUM=$[RANDOM%10]
while read -p "輸入 0-9 之間的數字: " INPUT ;do
 if [ $INPUT -eq $NUM ];then
 echo "恭喜你猜對了!"
 break
 elif [ $INPUT -gt $NUM ];then
        echo "數字太大了,重新猜!"
 else
        echo "數字太小了,重新猜!"
    fi
done           

4.2.6 循環控制 shift 指令

shift [n] 用于将參量清單 list 左移指定次數,預設為左移一次。

參量清單 list 一旦被移動,最左端的那個參數就從清單中删除。while 循環周遊位置參量清單時,常用到 shift

案例:doit.sh

[root@nginx ~]#cat doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while [ $You can't use 'macro parameter character #' in math mode# -gt 0 ] # or 
(( $# > 0 ))
do
 echo  $*
 shift
done
./doit.sh a b c d e f g h           

案例:

[root@nginx ~]#cat shift.sh
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ] 
do
 echo "$1"
 shift
done
echo   
./shfit.sh a b c d e f g h           

案例:

[root@nginx ~]#cat shift_batch_user.sh 
#!/bin/bash

if [ $# -eq 0 ];then
    echo "Usage: `basename $0` user1 user2 ..."
    exit
fi
                                       
while [ "$1" ];do
        if id $1 &> /dev/null;then
            echo $1 is exist
        else
           useradd $1
           echo "$1 is created"
        fi
       shift
done
echo "All user is created"

[root@nginx ~]#sh shift_batch_user.sh
Usage: shift_batch_user.sh user1 user2 ...
 
[root@nginx ~]#sh shift_batch_user.sh t1  a1  j1
t1 is exist
a1 is exist
j1 is created
All user is created           

4.2.7 while 特殊用法 while read

while 循環的特殊用法,周遊檔案或文本的每一行

格式:

while read line; do
 循環體
done < /PATH/FROM/SOMEFILE           

說明:依次讀取/PATH/FROM/SOMEFILE檔案中的每一行,且将行指派給變量line

案例:

[root@nginx ~]# echo test | read X ; echo $X

[root@nginx ~]#
[root@nginx ~]# echo test | read X ; echo $X

[root@nginx ~]# echo test | while read X ; do echo $X;done
test
[root@nginx ~]# echo test | { read X ; echo $X; }
test
[root@nginx ~]# echo test | ( read X ; echo $X )
test
[root@nginx ~]# echo zhangsan lisi wangwu | ( read X Y Z; echo $X $Y $Z )
zhangsan lisi wangwu
[root@nginx ~]# echo zhangsan lisi wangwu  | while read X Y Z; do echo $X $Y $Z;done
zhangsan lisi wangwu
[root@nginx ~]#           
[root@nginx ~]#cat while_read_diskcheck.sh
#!/bin/bash
WARNING=80
MAIL= #輸入你的郵箱
df |sed -nr  "/^\/dev\/sd/s#^([^ ]+) .* ([0-9]+)%.*#\1 \2#p"|while read DEVICE 
USE;do
 if [ $USE -gt $WARNING ] ;then
    echo "$DEVICE will be full,use:$USE" | mail -s "DISK WARNING" $MAIL
 fi
done           

案例:檢視/sbin/nologin的shell類型的使用者名和UID

[root@nginx ~]#cat while_read_passwd.sh
#!/bin/bash
while read line ;do
        if [[ "$line" =~ /sbin/nologin$ ]] ;then
                echo $line | cut -d: -f1,3
        fi                                                                       
                                
done < /etc/passwd           

4.2.8 循環與菜單 select

格式:

select NAME [in WORDS ... ;] do COMMANDS; done
select variable in list ;do 
 循環體指令
done           

說明:

  • select 循環主要用于建立菜單,按數字順序排列的菜單項顯示在标準錯誤上,并顯示 PS3 提示符,等待使用者輸入
  • 使用者輸入菜單清單中的某個數字,執行相應的指令
  • 使用者輸入被儲存在内置變量 REPLY 中
  • select 是個無限循環,是以要用 break 指令退出循環,或用 exit 指令終止腳本。也可以按 ctrl+c 退出循環
  • select 經常和 case 聯合使用
  • 與 for 循環類似,可以省略 in list,此時使用位置參量

案例:

[root@nginx ~]#cat select.sh 
#!/bin/bash
#
sum=0
PS3="請點菜(1-6): "
select MENU in 烤鴨 燒雞 小龍蝦 鮑魚 魚翅 點菜結束;do
case $REPLY in
1)
 echo $MENU 價格是 100
 let sum+=100
 ;;
2)
 echo $MENU 價格是 88
 let sum+=88
 ;;
3)
echo $MENU價格是 66
 let sum+=66
 ;;
4)
 echo $MENU 價格是 166
 let sum+=166
 ;;
5)
 echo $MENU 價格是 200
 let sum+=200
 ;;
6)
 echo "點菜結束,退出"
 break
 ;;
*)
 echo "點菜錯誤,重新選擇"
 ;;
esac
done
echo "總價格是: $sum"           
Linux-Shell腳本程式設計(四)

未完待續~~