天天看点

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脚本编程(四)

未完待续~~