shell腳本編程筆記(四)—— 流程控制之循環結構

循環命令主要有三種:for(for又分兩種)、while、until,另外有兩個控制循環提前結束的命令:continue、break。

 

一、 for循環

for循環有兩種格式,一種bash shell基本格式,一種C語言風格的形式。

1. bash shell的基本格式

for variable [in words]
do
    commands
done

for命令的強大之處在於可以通過多種方式來創建in後面的列表,例如:

  • 直接由你列出所有要循環的值
#!/bin/bash  
  
for i in f1 f2 f3 ;  
do  
echo $i is appoint ;  
done 
for str in 'This is a string'
do
    echo $str
done
  • 花括號創建 for i in {A..D}
#!/bin/bash  
  
for i in {1..10}  
do  
echo $(expr $i \* 3 + 1);  
done  
  • 路徑名展開 for in in dir*.txt
#!/bin/bash  
  
for file in /proc/*;  
do  
echo $file is file path \! ;  
done  
  • 命令替換     for i in $(seq 1 5) 或者 for i in `ls`
#!/bin/bash  
  
for i in $(seq 1 10)  
do   
echo $(expr $i \* 3 + 1);  
done
#!/bin/bash  
  
for i in `ls`;  
do   
echo $i is file name\! ;  
done 
  • 從文件中讀取(可更改IFS變量改變默認字段分隔符)
#!/bin/bash  

file="states"

IFS=$'\n'  #指定字段分隔符僅爲\n,默認爲空格、Tab與\n
for state in `cat $file`;  
do   
    echo "visit beautiful $state"  
done 

 

2. C語言風格

# for (())中變量不以$開頭
for (( 初始值; 循環條件; 迭代量 ))
do
    commands
done

例子

for (( i=0; i<5; i++ ))
do
    echo $i
done

也允許在初始值中使用多個變量,分別進行處理,但循環條件只能有一個

for (( i=0, j=10; i<5; i++, j-- ))
do
    echo "$i - $j"
done

 

二、 while循環

和if一樣,while也是利用命令的退出狀態判斷,只要退出狀態爲0,它就執行循環體內的內容

while commands
do
    commands
done

例子

#!/bin/bash

count=1
while [ $count -lt 5 ]
do
    echo $count
    count=$((count + 1))  # 也可以寫成 count=$(expr $count + 1)
done

創建多個用戶

#!/bin/bash

input="users.csv"
while IFS=',' read -r userid name
do
    echo "adding $userid"
    useradd -c "$name" -m $userid
done < "$input"

來看一個複雜點的例子(here-document語法裏的內容必須頂格寫,否則會報錯)

#!/bin/bash

DELAY=3 # 結果展示時長
while [[ $REPLY != 0 ]]; do # 當輸入值不爲0,清除屏幕內容,顯示菜單並執行對應命令
    clear
cat <<EOF
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
EOF
    read -p "Enter selection [0-3] > "
    if [[ $REPLY =~ ^[0-3]$ ]]; then # ~是對後面的正則表達式表示匹配的意思,匹配輸出1,不匹配輸出0
        if [[ $REPLY == 1 ]]; then
            echo "Hostname: $HOSTNAME"
            uptime
            sleep $DELAY
        fi
        if [[ $REPLY == 2 ]]; then
            df -h
            sleep $DELAY
        fi
        if [[ $REPLY == 3 ]]; then
            if [[ $(id -u) -eq 0 ]]; then
                echo "Home Space Utilization (All Users)"
                du -sh /home/*
            else
                echo "Home Space Utilization ($USER)"
                du -sh $HOME
            fi
            sleep $DELAY
        fi
    else
        echo "Invalid entry."
        sleep $DELAY
    fi
done
echo "Program terminated."

菜單包含在 while 循環中,每次用戶選擇之後,能夠讓程序重複顯示菜單。只要 REPLY 不爲0,循環就會繼續,菜單就能顯示,用戶可以重新選擇。每次動作完成之後,會執行一個 sleep 命令,所以在清空屏幕和重新顯示菜單之前,程序將會停頓幾秒鐘,爲的是能夠看到選項輸出結果。 一旦 REPLY 等於0,則表示選擇了“退出”選項,循環就會終止,程序繼續執行 done 語句之後的代碼。

 

三、 until循環

也是利用命令的退出狀態判斷,但與while相反,退出狀態不爲0時才執行循環體內的內容

until commands
do
    commands
done

簡單例子

#!/bin/bash

count=1
until [ $count -gt 5 ]
do
    echo $count
    count=$((count + 1))
done

 

四、 嵌套循環

上面幾種循環間都是可以相互嵌套的,可根據需要選擇,例如:

循環處理文件數據,結合前面提到的IFS環境變量,取出/etc/passwd各字段值

#! /bin/bash

IFS.OLD=$IFS
IFS=$'\n' #外層循環使用\n分段,解析出各行
for entry in $(cat /etc/passwd)
do
  echo "Values in $entry -"
  IFS=:   #內層循環使用:分段,解析行中各字段
  for value in $entry
  do
     echo "  $value"
  done
done  

 

五、 控制循環

如果一旦啓動循環就只能等到完成所有迭代,有時是非常低效的,在符合某些條件後有時可以跳出循環。跳出方法有兩種:break和continue。

1. break

默認是立刻結束本層循環,執行外層內容(可能是外層循環或其他命令)。

改寫下前面的例子,如果用戶名爲oracle,跳出內層value循環,不再輸出本行值,開始下一次外層entry循環

#! /bin/bash

IFS=$'\n' #外層循環使用\n分段,解析出各行
for entry in $(cat passwd)
do
  echo "Values in $entry -"
  IFS=:   #內層循環使用:分段,解析行中各字段
  for value in $entry
  do
     if [ -z $value ] #由於:之間可能存在空字符串,需要額外加個判斷,否則執行==比較會報錯
     then echo "  $value"
     elif [ $value == 'oracle' ]
     then
       break  #跳出內層循環
     else
     echo "  $value"
     fi
  done
done  

可以看到oracle那一行沒有輸出具體字段值

其實break還可以指定要跳出哪層循環,默認爲1,即當前循環。指定方式爲

break n

稍微改寫一下前面的腳本,改爲跳出外層(即break 2)循環

#! /bin/bash

IFS=$'\n'
for entry in $(cat passwd)
do
  echo "Values in $entry -"
  IFS=:
  for value in $entry
  do
     if [ -z $value ]
     then echo "  $value"
     elif [ $value == 'oracle' ]
     then
       break 2 #修改爲跳出外層循環
     else
     echo "  $value"
     fi
  done
done  

可以看到只執行到輸出Values in oracle那行,下面的字段輸出及外層的循環都沒有再執行了(若執行外層循環還會輸出grid用戶相關信息)

 

2. continue

默認結束本層本次循環,開始執行本層下一次循環。還是用剛纔的例子:

#! /bin/bash

IFS=$'\n'
for entry in $(cat passwd)
do
  echo "Values in $entry -"
  IFS=:
  for value in $entry
  do
     if [ -z $value ]
     then echo "  $value"
     elif [ $value == 'oracle' ]
     then
       continue
     else
     echo "  $value"
     fi
  done
done  

可以看到沒有輸出oracle這個字段值,其餘都正常輸出了

其實continue也可以指定要繼續執行哪層循環,默認爲1,即當前循環。指定方式爲

continue n

稍微改寫一下前面的腳本,改爲繼續執行外層循環,即跳出本層循環(continue 2)

#! /bin/bash

IFS=$'\n'
for entry in $(cat passwd)
do
  echo "Values in $entry -"
  IFS=:
  for value in $entry
  do
     if [ -z $value ]
     then echo "  $value"
     elif [ $value == 'oracle' ]
     then
       continue 2
     else
     echo "  $value"
     fi
  done
done  

可以看到跟默認的break一個效果。

 

六、 循環結果處理

如果要將循環輸出的結果輸入文件,或者通過管道傳給其他命令,應該在done後面操作。

#!/bin/bash

count=1
while [ $count -lt 5 ]
do
    echo $count
    count=$((count + 1))  # 也可以寫成 count=$(expr $count + 1)
done > output.txt
#!/bin/bash  
  
for file in /proc/*;  
do  
echo $file is file path \! ;  
done | sort

 

參考

https://www.cnblogs.com/regit/p/11113373.html

https://www.jianshu.com/p/0beca03deff1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章