shell指令碼之迴圈

Harda發表於2024-10-14

迴圈基礎知識

在 Shell 指令碼中,迴圈是一種控制結構,用於重複執行一段程式碼多次。常用的有 for 迴圈、while迴圈、until迴圈,三者之間的效能差異不是特別明顯,主要取決於具體的使用場景和迴圈體的複雜性。for 迴圈用於遍歷固定數量的元素(如陣列、列表等),通常用在已知迭代次數的場景。while 迴圈在條件為真時執行,而 until 迴圈在條件為假時執行。這意味著它們在邏輯上是相對的,選擇哪種迴圈更多是基於程式設計的可讀性。

for 迴圈

for迴圈基礎

for 迴圈的語法如下所示
for variable in listdo    # 需要執行的命令done

示例:

[root@imzcy ~]# cat f.sh#!/bin/bash
for VAR in 1 2 3 4 5do
echo "[$(date +%Y%m%d_%H%M%S)] 當前迴圈中 VAR 的值為:$VAR" sleep 1
done[root@imzcy ~]#
簡單來說 for 迴圈會將 in 後面的所有空格分隔的內容都作為單個引數一個個讀入進來順序執行完。
[root@imzcy ~]# sh f.sh[20240817_113851] 當前迴圈中 VAR 的值為:1[20240817_113852] 當前迴圈中 VAR 的值為:2[20240817_113853] 當前迴圈中 VAR 的值為:3[20240817_113854] 當前迴圈中 VAR 的值為:4[20240817_113855] 當前迴圈中 VAR 的值為:5[root@imzcy ~]#

前面講過 for 迴圈通常用在已知迭代次數的場景中,接下來我們給出一些實際案例。

for迴圈之基於數值的迴圈

主要用在想要迴圈指定次數的場景中,透過 seq 命令或者 {} 都可以實現。

透過 seq 命令
[root@imzcy ~]# cat f2.sh#!/bin/bash
for VAR in $( seq 1 5 )do
echo "[$(date +%Y%m%d_%H%M%S)] 當前迴圈中 VAR 的值為:$VAR" sleep 1
done[root@imzcy ~]#
[root@imzcy ~]# sh f2.sh[20240817_115517] 當前迴圈中 VAR 的值為:1[20240817_115518] 當前迴圈中 VAR 的值為:2[20240817_115519] 當前迴圈中 VAR 的值為:3[20240817_115520] 當前迴圈中 VAR 的值為:4[20240817_115521] 當前迴圈中 VAR 的值為:5[root@imzcy ~]#

透過 {} 實現

[root@imzcy ~]# cat f3.sh#!/bin/bash
for VAR in {1..5}do
echo "[$(date +%Y%m%d_%H%M%S)] 當前迴圈中 VAR 的值為:$VAR" sleep 1
done[root@imzcy ~]#
[root@imzcy ~]# sh f3.sh[20240817_115748] 當前迴圈中 VAR 的值為:1[20240817_115749] 當前迴圈中 VAR 的值為:2[20240817_115750] 當前迴圈中 VAR 的值為:3[20240817_115751] 當前迴圈中 VAR 的值為:4[20240817_115752] 當前迴圈中 VAR 的值為:5[root@imzcy ~]#

for迴圈之基於字串的迴圈

主要用在想要迴圈所有已知字元的情況下,可以為空格分隔的多個字串或命令的執行結果。

直接指定空格分隔的多個字串生成json資料
[root@imzcy ~]# cat f5.sh#!/bin/bash
for VAR in zhangsan lisi wangwudo
JSON_DATA='{"name":"'"${VAR}"'","group":"test-001"}' echo "[$(date +%Y%m%d_%H%M%S)] 資料為 '$JSON_DATA'" sleep 1
done[root@imzcy ~]#
[root@imzcy ~]# sh f5.sh[20240817_122120] 資料為 '{"name":"zhangsan","group":"test-001"}'[20240817_122121] 資料為 '{"name":"lisi","group":"test-001"}'[20240817_122122] 資料為 '{"name":"wangwu","group":"test-001"}'[root@imzcy ~]#
使用命令的執行結果建立備份檔案
[root@imzcy ~]# ls *.txtexample.txt  group.txt  test.txt  user.txt[root@imzcy ~]#
[root@imzcy ~]# cat f4.sh#!/bin/bash
for VAR in $( ls /root/*.txt )do
NEW_VAR="${VAR}.bak" echo "[$(date +%Y%m%d_%H%M%S)] $VAR 檔案將備份為 $NEW_VAR" cp $VAR $NEW_VAR sleep 1
done[root@imzcy ~]#
[root@imzcy ~]# sh f4.sh[20240817_120453] /root/example.txt 檔案將備份為 /root/example.txt.bak[20240817_120454] /root/group.txt 檔案將備份為 /root/group.txt.bak[20240817_120455] /root/test.txt 檔案將備份為 /root/test.txt.bak[20240817_120456] /root/user.txt 檔案將備份為 /root/user.txt.bak[root@imzcy ~]#[root@imzcy ~]# ls *.bakexample.txt.bak  group.txt.bak  test.txt.bak  user.txt.bak[root@imzcy ~]#

while 迴圈

while 迴圈基礎

while 迴圈在條件為真時執行,語法格式如下所示
while [ 條件 ]do    # 需要執行的命令done
示例
[root@imzcy ~]# cat f6.sh#!/bin/bash
VAR=1while [ $VAR -le 5 ]do
echo "VAR: $VAR" ((VAR++))
done[root@imzcy ~]#
變數VAR的初始值為1,每次迴圈後自增1,當迴圈了5次後值為6的時候,條件 VAR 值小於等於5 不為真,則退出迴圈。
[root@imzcy ~]# sh f6.shVAR: 1VAR: 2VAR: 3VAR: 4VAR: 5[root@imzcy ~]#
while 迴圈之死迴圈前面講過 while 迴圈只在 “條件” 為真的時候執行,所以如果想要迴圈一直持續執行的話,最簡單就是條件直接為 true 。
[root@imzcy ~]# cat f7.sh#!/bin/bash
while truedo
ping -c 1 -W 2 example.com >/dev/null 2>&1 if [ $? -ne 0 ] ; then
echo "[ $(date +%Y%m%d_%H%M%S) ] 到 example.com 的網路不通!"
fi sleep 1
done[root@imzcy ~]#
while 迴圈之讀取檔案內容while 迴圈還可以用於讀取檔案中的內容,進行資料處理。
[root@imzcy ~]# cat ip.txt192.168.2.11 1723940173192.168.2.12 1723942573192.168.2.13 1723947373[root@imzcy ~]#[root@imzcy ~]# cat f8.sh#!/bin/bash
INPUT_FILE=/root/ip.txt
while read NOW_LINEdo
echo "當前行為:$NOW_LINE" eval $(echo "$NOW_LINE" |awk '{printf("KEY_IP=%s; KEY_TIME=%s",$1,$2)}') NEW_TIME=$( date -d @${KEY_TIME} "+%Y-%m-%d %H:%M:%S" ) echo "將時間戳轉換為標準時間後行為:[$NEW_TIME] $KEY_IP" sleep 1
done < $INPUT_FILE[root@imzcy ~]#
[root@imzcy ~]# sh f8.sh當前行為:192.168.2.11 1723940173處理後資料為:[2024-08-18 08:16:13] 192.168.2.11當前行為:192.168.2.12 1723942573處理後資料為:[2024-08-18 08:56:13] 192.168.2.12當前行為:192.168.2.13 1723947373處理後資料為:[2024-08-18 10:16:13] 192.168.2.13[root@imzcy ~]#

while 迴圈之讀取變數值

如果變數的值為多行內容,也可以直接 while 迴圈讀取進行處理。

[root@imzcy ~]# cat f9.sh#!/bin/bash
USER_LIST="張三 zhangsan@example.com李四 lisi@example.com王五 wangwu@example.com"
echo "$USER_LIST" | while read NOW_LINEdo
eval $(echo "$NOW_LINE" |awk '{printf("KEY_USER=%s; KEY_MAIL=%s",$1,$2)}') echo "即將給使用者 ${KEY_USER} 的郵箱 ${KEY_MAIL} 傳送郵件。" sleep 1
done[root@imzcy ~]#
[root@imzcy ~]# sh f9.sh即將給使用者 張三 的郵箱 zhangsan@example.com 傳送郵件。即將給使用者 李四 的郵箱 lisi@example.com 傳送郵件。即將給使用者 王五 的郵箱 wangwu@example.com 傳送郵件。[root@imzcy ~]#
使用上面的方式讀取變數中的內容處理資料時,由於使用了管道符號 | ,因此會建立一個新的子shell來執行管道右側的命令,子shell中所做的任何更改(包括變數賦值)都不會影響到主shell。所以這種情況下,while 迴圈中任何的變數賦值以及變數內容的修改都無法傳遞到迴圈外的後續命令的執行。這點需要額外注意。

until 迴圈

until 迴圈基礎

until 迴圈在條件為假時執行,語法格式如下所示

until [ 條件 ]do    # 需要執行的命令done

示例

[root@imzcy ~]# cat f10.sh#!/bin/bash
VAR=0until [ $VAR -eq 8 ]do
read -p "請輸入您猜測的數字:" VAR sleep 1
done[root@imzcy ~]#

當使用者輸入錯誤的數字時會一直要求重新輸入,直到輸入正確的數字時才會退出迴圈。

[root@imzcy ~]# sh f10.sh請輸入您猜測的數字:1請輸入您猜測的數字:2請輸入您猜測的數字:6請輸入您猜測的數字:8[root@imzcy ~]#

正常情況下一般 while 迴圈用的比較多一些,但是當你需要等待某個條件變為真時,使用 until 迴圈是一個理想的選擇。例如,等待某個程序啟動,或者等待某個檔案的存在。

until 迴圈之等待某程序啟動

如下指令碼使用 pgrep 命令作為條件,當指定名稱的程序沒有在執行時,會一直迴圈等待直到條件為真時結束迴圈。

[root@imzcy ~]# cat f11.sh#!/bin/bash
PROCESS_NAME="$1"
until pgrep -x "$PROCESS_NAME" > /dev/nulldo
echo "程序 $PROCESS_NAME 未執行。請等待此程序啟動!" sleep 1
done[root@imzcy ~]#
由於不存在 dotnet 程序,所以一直迴圈直到我 <ctrl>+<C> 終止。
[root@imzcy ~]# sh f11.sh dotnet程序 dotnet 未執行。請等待此程序啟動!程序 dotnet 未執行。請等待此程序啟動!程序 dotnet 未執行。請等待此程序啟動!^C[root@imzcy ~]#

由於 mysqld 程序已經在啟動了,所以迴圈直接退出執行了。

[root@imzcy ~]# sh f11.sh mysqld[root@imzcy ~]#

break 和 continue 指令

上面講到的三個迴圈語句中有兩個通用的指令,break 和 continue。break 用於中止當前迴圈的執行。當 break 被執行時,迴圈將被立即終止,程式的控制權將轉移到迴圈後面的程式碼。而 continue 用於跳過當前迴圈的剩餘部分,並立即開始下一次的迴圈迭代。也就是說,當 continue 被執行時,控制權將轉到迴圈的條件部分,而不執行隨後的程式碼。

迴圈之 break 指令測試

[root@imzcy ~]# cat f12-1.sh#!/bin/bash
for i in {1..10}; do if [ $i -eq 5 ]; then break fi echo $idone[root@imzcy ~]#

如下所示,當 i 的值迴圈到 5 時,則迴圈被終止。

[root@imzcy ~]# sh f12-1.sh1234[root@imzcy ~]#
迴圈之 continue 指令測試
[root@imzcy ~]# cat f12-2.sh#!/bin/bash
for i in {1..10}; do if [ $i -eq 5 ]; then continue fi echo $idone[root@imzcy ~]#

如下所示,當 i 的值迴圈到 5 時,則跳過迴圈中後續命令的執行(沒有列印數字5)直接開始下次迴圈。

[root@imzcy ~]# sh f12-2.sh1234678910[root@imzcy ~]#

來源於:https://mp.weixin.qq.com/s/ulB4DE4S8bElcJLS9kug1A

相關文章