Shell 腳本編程: for 循環與 select 循環
循環語句命令常用于重復執行一條指令或一組指令,直到條件不再滿足時停止,Shell腳本語言的循環語句常見的有while、until、for及select循環語句。
本文將詳細介紹Shell編程中for循環和select循環的各種應用場景和實踐技巧。
for 循環語句介紹
for 循環是 Shell 編程中常用的循環結構,主要用于執行次數有限的循環任務。與 while 循環不同,for 循環更適合處理已知迭代次數的場景。for 循環主要有兩種語法結構:變量取值型和C語言型。
變量取值型 for 循環
語法結構:
for 變量名 in 變量取值列表
do指令...
done
說明:
in 變量取值列表
可以省略,省略時相當于in "$@"
- 執行過程:變量名依次獲取取值列表中的每個值,執行循環體內的指令
執行流程:
- 變量獲取列表中的第一個值
- 執行循環體內的指令
- 執行到 done 后結束本次循環
- 變量獲取列表中的下一個值,重復上述過程
- 直到取完列表中所有值
C 語言型 for 循環
語法結構:
for ((exp1; exp2; exp3))
do指令...
done
說明:
- exp1:變量初始化(如:i=0)
- exp2:循環條件(如:i<100)
- exp3:變量更新(如:i++)
執行流程:
- 執行初始化表達式
- 檢查條件表達式,如果為真則進入循環
- 執行循環體內的指令
- 執行變量更新表達式
- 重復步驟2-4,直到條件表達式為假
for 循環基礎實踐示例
示例1:豎向升序打印數字
#!/bin/bash
# 列表型for循環
for i in {1..5}
doecho $i
done# 使用seq命令
for i in $(seq 5)
doecho $i
done# C語言型for循環
for ((i=1; i<=5; i++))
doecho $i
done# while循環實現相同功能
i=1
while ((i<=5))
doecho $i((i++))
done
示例2:豎向降序打印數字
#!/bin/bash
# 使用序列表達式
for i in {5..1}
doecho $i
done# 使用seq命令帶步長
for i in $(seq 5 -1 1)
doecho $i
done
示例3:求和與求乘積
#!/bin/bash
# 求1-10的和
sum=0
for i in {1..10}
dosum=$[ sum + i ] # 累加計算
done
echo $(seq -s '+' 10)=$sum # 顯示計算過程# 求1-10的乘積
sum=1
for i in {1..10}
do((sum *= i)) # 累乘計算
done
echo $(seq -s '*' 10)=$sum # 顯示計算過程
示例4:求水仙花數
水仙花數是指一個 3 位數,它的每個位上的數字的 3次冪之和等于它本身。
#!/bin/bash
for num in {100..999}
don1=$[num/100] # 獲取百位數n2=$[num%100/10] # 獲取十位數n3=$[num%10] # 獲取個位數# 判斷是否為水仙花數if [ $[ n1**3 + n2**3 + n3**3 ] -eq $num ];thenecho $n1^3 + $n2^3 + $n3^3 = $n1$n2$n3fi
done
示例5:求素數
素數又稱質數,是指除了 1 和它本身以外,不能被任何整數整除的數。
#!/bin/bash
echo -n "100~200之間所有的素數為:"
for ((i=100; i<=200; i++))
do# 除數的范圍是2和本身-1的數for ((j=2; j<=i-1; j++))do# 如果能整除,說明不是質數,跳出本次循環if [ $[i%j] -eq 0 ];thenbreakfi# 如果本次循環到最后都不能整除,則是質數if [ $j -eq $[i-1] ];thenecho -n " $i"fidone
done
echo
執行結果:
100~200之間所有的素數為: 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199
示例6:九九乘法表
#!/bin/bash
for num1 in {1..9}
dofor ((num2=1; num2<=num1; num2++))do# 格式化輸出,使用制表符對齊echo -en "$num2*$num1=$[ num2 * num1 ]\t"doneecho # 換行
done
示例7:求出滿足條件的4位數
解題思路:
- 余數是5,除數肯定大于5,至少是6
- 大于等于6的個位數乘以一個數,結果還是一位數,則商上面的每個數都是1
解法1:從1000開始枚舉到9999
#!/bin/bash
# num是被除數
for num in {1000..9999}
do# i是除數for i in {6..9}do# 分解數字的各位num1=$[$num/1000] # 千位num2=$[$num/100%10] # 百位num3=$[$num/10%10] # 十位num4=$[$num%10] # 個位e=$[num1*10+num2-i] # 第一次計算if ((e<10));thenf=$[e*10+num3-i] # 第二次計算if ((f<10 && $[f*10+num4-i]==5));thenecho "${i}x111+5=$num" # 輸出結果fifidone
done
解法2:分別對每個數進行枚舉
#!/bin/bash
for ((i=6; i<=9; i++)) # 除數范圍
dofor ((a=1; a<=9; a++)) # 千位數dofor ((b=0; b<=9; b++)) # 百位數doe=$[a*10+b-i] # 第一次計算# e是個位數if [ $e -lt 10 ];thenfor ((c=0; c<=9; c++)) # 十位數do# f是個位數f=$[e*10+c-i] # 第二次計算if [ $f -lt 10 ];thenfor ((d=0; d<=9; d++)) # 個位數doif [ $[f*10+d-i] -eq 5 ];then # 最終余數應為5echo "${i}x111+5=$[i*111+5]" # 輸出結果fidonefidonefidonedone
done
性能對比:
$ time bash compute1.sh
9x111+5=1004
real 0m0.986s$ time bash compute2.sh
9x111+5=1004
real 0m0.014s
**思考:**compute2.sh時間更短是因為它通過數學分析減少了循環次數,直接從可能的除數開始枚舉,避免了不必要的計算。
示例8:百錢買百雞
中國古代數學家張丘建在他的《算經》中提出了著名的"百錢買百雞問題":
- 雞翁一,值錢五
- 雞母一,值錢三
- 雞雛三,值錢一
- 百錢買百雞,問翁、母、雛各幾何?
#!/bin/bash
# 雞翁數量 cock_num
# 雞母數量 hen_num
# 雞雛數量 chick_num
# 雞總數量 sum
sum=100
# 錢總數 money
money=100for ((cock_num=1; cock_num<=money/5; cock_num++))
dofor ((hen_num=1; hen_num<=money/3; hen_num++))dofor chick_num in {3..100..3} # 雞雛數是3的倍數do# 判斷數量和金額是否都滿足條件if [ $[ cock_num+hen_num+chick_num ] -eq $sum -a \$[ cock_num*5+hen_num*3+chick_num/3 ] -eq $money ];thenecho "雞翁:$cock_num 雞母:$hen_num 雞雛:$chick_num"echo "$cock_num*5+$hen_num*3+$chick_num/3=100"echofidonedone
done
執行結果:
雞翁:4 雞母:18 雞雛:78
4*5+18*3+78/3=100雞翁:8 雞母:11 雞雛:81
8*5+11*3+81/3=100雞翁:12 雞母:4 雞雛:84
12*5+4*3+84/3=100
示例9:三對情侶參加婚禮
3個新郎為A、B、C,3個新娘為X、Y、Z。得到的假話信息:
- A說他將和X結婚
- X說她的未婚夫是C
- C說他將和Z結婚
解題思路:
- 根據假話得出反向判定條件
- 多個人不能同時和一個人結婚
解法1:直接枚舉
#!/bin/bash
for A in X Y Z
dofor B in X Y Zdofor C in X Y Zdo# 判斷條件:所有人不能重復,且與假話相反if [ "$A" != "$B" -a "$A" != "$C" -a "$B" != "$C" -a \"$A" != "X" -a "$C" != "X" -a "$C" != "Z" ];thenecho "A 與 $A 結婚"echo "B 與 $B 結婚"echo "C 與 $C 結婚"fidonedone
done
解法2:使用數字表示,更高效
#!/bin/bash
# 將X Y Z 抽象為 1 2 3
# 定義函數,根據參數值返回對應字母
duixiang () {case $1 in1) echo X ;;2) echo Y ;;3) echo Z ;;esac
}for ((A=1; A<=3; A++))
dofor ((B=1; B<=3; B++))dofor ((C=1; C<=3; C++))do# 多個人不能同時和一個人結婚# 根據假話得出反向判定條件if [ $A -ne 1 -a $C -ne 1 -a $C -ne 3 -a \$A -ne $B -a $A -ne $C -a $B -ne $C ];thenecho "A 與 $(duixiang $A) 結婚"echo "B 與 $(duixiang $B) 結婚"echo "C 與 $(duixiang $C) 結婚"exitfidonedone
done
執行結果:
A 與 Z 結婚
B 與 X 結婚
C 與 Y 結婚
示例10:求a+aa+aaa+…aaaaa
例如:2+22+222+2222+22222(此時共有5個數相加),數字和數字最長位數由鍵盤輸入。
#!/bin/bash
read -p "輸入1-9之間一個數:" m
read -p "輸入數字的最長位數:" n# 定義最終和
sum=0for ((i=1; i<=n; i++))
do# count是j位m的值,每次計算完count值設置為0count=0for ((j=1; j<=i; j++))do# 計算j位m的值count=$[count + m * 10**(j-1)]doneecho -n "$count "# 控制輸出格式if [ $i -eq $n ];thenecho -n "= "elseecho -n "+ "fisum=$[sum + count]
done
echo $sum
執行結果:
輸入1-9之間一個數:6
輸入數字的最長位數:5
6 + 66 + 666 + 6666 + 66666 = 74070
示例11:排列組合
有1、2、3、4個數字,能組成多少個互不相同且無重復數字的三位數?都是多少?
#!/bin/bash
# 組合數量為sum
sum=0echo -n "組合包括:"
for ((i=1; i<=4; i++))
dofor ((j=1; j<=4; j++))dofor ((k=1; k<=4; k++))do# 確保三位數互不相同if [ $i -ne $j -a $i -ne $k -a $j -ne $k ];thenecho -n "$i$j$k "sum=$[sum+1]fidonedone
done
echo
echo "組合總數量為:$sum"
執行結果:
組合包括:123 124 132 134 142 143 213 214 231 234 241 243 312 314 321 324 341 342 412 413 421 423 431 432
組合總數量為:24
for 循環語句實踐案例
示例1:批量修改文件擴展名
# 創建測試文件
touch file-{1..3}.txt# 方法1:使用sed獲取新文件名
#!/bin/bash
for file in $(ls *.txt)
donew_name=$(echo $file | sed 's/.txt$/.jpg/') # 替換擴展名mv $file ${new_name} # 重命名文件
done# 方法2:使用rename命令(更高效)
rename ".jpg" ".txt" *.jpg # 批量修改擴展名
rename "file-" "" *.txt # 刪除文件名中的特定字符串
示例2:批量設置服務開機啟動
#!/bin/bash
for service in crond atd chronyd
dosystemctl enable $service # 設置服務開機啟動
done
示例3:批量重啟多個主機sshd服務
#!/bin/bash
for host in server{1..10}
dossh root@$host systemctl restart sshd # 遠程重啟服務
done
示例4:批量備份文件
#!/bin/bash
for file in host*
docp $file /backup/${file}.bak # 備份文件并添加.bak后綴
done
示例5:掃描網絡中在線主機
#!/bin/sh
for i in `seq 1 254`
doIP=172.25.254.$ping -c2 $IP &> /dev/null # 發送2個ping包if [ $? -eq 0 ];thenecho "$IP is online" >> node_online.txtelse echo "$IP is offline" >> node_offline.txtfi
done# 注意:添加&符號可實現并發掃描
示例6:監控主機磁盤使用情況
#!/bin/bash
hostfile=/tmp/hosts
if [ $(cat $hostfile | wc -l) -lt 1 ];thenecho $hostfile is empty.exit 1
fifor host in $(cat $hostfile)
do> disk.log # 清空日志文件echo "=============HOSTNAME: $host =============" >> disk.log# 獲取磁盤使用率超過80%的分區ssh $host df -h | grep '^/dev/' | awk '{print $1,$5}' | \cut -f 1 -d% | awk '{if ($2 > 80) print $0}' >> disk.log# 如果有超過80%的分區,發送郵件報警if [ $(cat disk.log | wc -l) -gt 1 ];thencat disk.log | mail -s "$host: disk is greater than 80%" root@localhostfi
done
for 循環語句高級實戰案例
示例1:MySQL數據庫備份
# 創建數據庫
#!/bin/bash
MYSQL_CMD='mysql -uroot -e '
for db in web1 web2
do${MYSQL_CMD} "create database $db;" # 創建數據庫
done# 備份數據庫
#!/bin/bash
MYSQL_CMD='mysqldump -uroot '
for db in web1 web2
do${MYSQL_CMD} $db > $db.sql # 備份數據庫到文件
done
示例2:MySQL分庫分表備份
# 創建表
#!/bin/bash
MYSQL_CMD='mysql -uroot $db -e '
for db in web1 web2
do# 創建表并插入測試數據${MYSQL_CMD} "create table test(id int,name varchar(16));insert into test values(1,'testdata')";
done# 備份表
#!/bin/bash
MYSQL_CMD='mysqldump -uroot $db'
for db in web1 web2
do${MYSQL_CMD} > ${db}_test.sql # 備份表數據
done
示例3:批量創建系統賬號
#!/bin/bash
# 引入系統函數庫
. /etc/init.d/functionsusers_info_file=/etc/users
users_list_file=/tmp/users[ ! -f $users_info_file ] && touch $users_info_fileexec < ${users_list_file} # 重定向輸入
# 創建用戶并設置密碼
while read username
do# 生成隨機密碼(使用md5加密并截取)pass=$(echo "$user+$RANDOM" | md5sum | cut -c 3-11)# 添加用戶并設置密碼useradd $username &>/dev/null && chage -d 0 $username && \echo "$pass" | passwd --stdin $username &>/dev/null && \echo -e "username: $username \t password:$pass" >> $users_info_file# 根據返回值判斷用戶和密碼是否添加成功if [ $? -eq 0 ];thenaction "$username is ok" /bin/true # 優雅顯示成功elseaction "$username is fail" /bin/false # 優雅顯示失敗fi
doneecho "--------------------------"
cat $users_info_file # 顯示創建的用戶信息
Linux 系統產生隨機數的6種方法
方法1:通過系統環境變量 RANDOM
echo "furongwang$RANDOM" | md5sum | cut -c 1-8 # 結合md5增強隨機性
方法2:通過openssl產生隨機數
openssl rand -base64 8 # 生成8位隨機Base64字符串
openssl rand -base64 20 # 生成20位隨機Base64字符串
方法3:通過date命令獲得隨機數
date +%s%N # 輸出納秒時間戳
方法4:通過設備文件生成隨機數
head /dev/urandom | md5sum # 使用隨機設備生成隨機數
head /dev/random | md5sum # 使用高質量隨機設備
方法5:通過UUID生成隨機數
cat /proc/sys/kernel/random/uuid # 讀取UUID
uuidgen # 生成UUID
方法6:使用expect附帶的mkpasswd命令
mkpasswd -l 20 -d 4 -c 4 -C 4 -s 4 # 生成符合復雜度要求的密碼
方法7:使用mktemp命令和sha1sum
mktemp -u | sha1sum # 創建臨時文件路徑并計算哈希值
select 循環語句介紹及語法
select循環主要用于創建交互式菜單,其語法結構如下:
select 變量名 [ in 菜單取值列表 ]
do指令...
done
特點:
in 菜單取值列表
可省略,省略時相當于使用in "$@"
- 執行后會顯示數字序號菜單,等待用戶選擇
- 用戶輸入數字序號后執行相應循環體
- 默認提示符為
#?
,可通過PS3環境變量自定義 - 用戶選擇的序號保存在REPLY環境變量中
- select是無限循環,需要顯式退出
select 循環語句案例
示例1:打印課程清單
#!/bin/bash
select course in mysql python linux
doecho $course # 輸出選擇的課程
done
示例2:更改提示符并配合case使用
#!/bin/bash
PS3="選擇你的課程: " # 自定義選擇提示符
select course in linux mysql python exit
doecho "你選擇了第 $REPLY 個條目。" # REPLY保存用戶選擇的序號case $course inlinux)echo "linux xxx";;mysql)echo "mysql yyy";;python)echo "python zzz";;exit)exit # 退出循環;;esacecho
done
示例3:兩個數計算器
#!/bin/bash
num1=10
num2=20
echo "There are two numbers $num2 and $num1."select method in 加法 減法 乘法 退出
docase $method in加法)echo $[num1 + num2] # 加法運算;;減法)echo $[num2 - num1] # 減法運算;;乘法)echo $[num1 * num2] # 乘法運算;;退出)exit # 退出程序;;*)echo "從1-4中選擇" # 錯誤輸入提示;;esac
done
示例4:一鍵安裝mysql和httpd
#!/bin/bash
# 定義輸出顏色函數
function print () {if [ "$1" == "PASS" ];thenecho -e '\033[1;32mPASS\033[0;39m' # 綠色elif [ "$1" == "FAIL" ];thenecho -e '\033[1;31mFAIL\033[0;39m' # 紅色elif [ "$1" == "DONE" ];thenecho -e '\033[1;35mDONE\033[0;39m' # 紫色elseecho "Usage: print PASS|FAIL|DONE"fi
}# 定義服務安裝函數
function InstallService () {case $1 inhttpd)echo -n "Start installing httpd..."{ yum install -y httpd # 安裝httpdsystemctl enable httpd --now # 設置開機啟動并立即啟動echo "Hello World" > /var/www/html/index.html # 創建測試頁面} &>/dev/null # 屏蔽輸出[ $? -eq 0 ] && print DONE || print FAIL # 判斷執行結果;;mysql)echo -n "Start installing mysql..."{ yum install -y mariadb-server # 安裝mariadbsystemctl enable mariadb --now # 設置開機啟動并立即啟動} &>/dev/null # 屏蔽輸出[ $? -eq 0 ] && print DONE || print FAIL # 判斷執行結果;;esac
}# 主菜單
select app in "Install httpd" "Install mysql" "Exit"
docase $REPLY in1)InstallService httpd # 安裝httpd;;2)InstallService mysql # 安裝mysql;;3)exit # 退出程序;;*)echo "從1-3中選擇。" # 錯誤輸入提示;;esac
done
總結
for循環的核心要點:
- 兩種語法結構:變量取值型和C語言型,各有適用場景
- 擅長處理已知迭代次數的循環任務
- 可以嵌套使用解決復雜問題
- 在企業環境中常用于批量操作、監控和自動化任務
select循環的核心要點:
- 專為創建交互式菜單設計
- 配合case語句可以實現復雜的交互邏輯
- 通過PS3和REPLY環境變量自定義提示符和獲取用戶選擇
- 適合編寫用戶友好的命令行工具