續接上回。。。
4 流程控制
4.1 條件選擇
4.1.1 條件判斷分紹
4.1.1.1 單分支條件
4.1.1.2 多分支條件
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 循環執行介紹
将某代碼段重複運作多次,通常有進入循環的條件和退出循環的條件
重複運作次數
- 循環次數事先已知
- 循環次數事先未知
常見的循環的指令:for, while, until
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++))
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"
未完待續~~