Shell脚本编程之流程控制

条件选择

在这里插入图片描述

选择执行if语句

[root@leiRocky data]# help if
if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
    Execute commands based on conditional.
    
    The `if COMMANDS' list is executed.  If its exit status is zero, then the
    `then COMMANDS' list is executed.  Otherwise, each `elif COMMANDS' list is
    executed in turn, and if its exit status is zero, the corresponding
    `then COMMANDS' list is executed and the if command completes.  Otherwise,
    the `else COMMANDS' list is executed, if present.  The exit status of the
    entire construct is the exit status of the last command executed, or zero
    if no condition tested true.
    
    Exit Status:
    Returns the status of the last command executed.

单分支

if 判断条件; then
   条件为真的分支代码
fi

双分支

if 判断条件; then
   条件为真的分支代码
else
   条件为假的分支代码
fi

多分支

if 判断条件1; then
    条件1为真的分支代码
elif 判断条件2; then
    条件2为真的分支代码
elif 判断条件3; then
    条件3为真的分支代码
...
else
    以上条件都为假的分支代码
fi

说明:
1.多个条件时,逐个条件进行判断,第一次遇为“真条件时,执行其分支,而后结束整个if语句
2. if 语句可嵌套

判断身体质量指数(BMI)

BMI计算公式:

BMI=体重(以千克为单位)除以身高的平方(以米为单位)。

例如:一个人的身高为1.75米,体重为68千克,他的BMI=68/(1.75*1.75)=22.2。

中国成人正常的BMI应在18.5-23.9之间,如果小于18.5为体重不足,如果大于等于24为超重,大于等于28为肥胖。

vim 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 2;
fi

BMI=`echo $WEIGHT/$HIGH^2|bc`

if [ $BMI -le 18 ]; then
    echo "太瘦了,多吃点!"
elif [ $BMI -lt 24 ]; then
    echo "身材很棒!"
else
    echo "太胖了,注意节食,加强运动!"
fi

在这里插入图片描述

脚本调错

在这里插入图片描述
bash -x 可以一行一行的调试脚本。

bash -x if_bmi.sh

在这里插入图片描述

条件判断case语句

[root@leiRocky scripts]# help case
case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
    Execute commands based on pattern matching.
    
    Selectively execute COMMANDS based upon WORD matching PATTERN.  The
    `|' is used to separate multiple patterns.
    
    Exit Status:
    Returns the status of the last command executed.
case 变量引用 in 
PAT1)
   分支1
   ;;
PAT2)
   分支2
   ;;
...
*)
   默认分支
   ;;
esac
case支持glob风格的通配符:
  *  任意长度任意字符
  ?  任意单个字符
  [] 指定范围内的任意单个字符 
  |  或者,如:a|b

范例
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

在这里插入图片描述

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

在这里插入图片描述

范例:文件后缀处理
vim file_suffix.sh

#! /bin/bash
read -p "please input a file: " FILE 

SUFFIX=`echo $FILE | grep -Eo "[^.]+$"`
case $SUFFIX in
gz)
    echo gzip
    ;;
bz2)
    echo bzip2
    ;;
xz)
    echo xz
    ;;
z)
    echo compress
    ;;
zip)
    echo zip
    ;;
*)
    echo other
    ;;
esac

在这里插入图片描述

运维菜单实现版本V2

vim sre_meun.sh

#! /bin/bash
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
请选择:
1)备份数据库
2)清理日志
3)软件升级
4)软件回滚
5)删库跑路
EOF
echo -en '\E[Om'

read -p "请输入上面数字1-5: " MENU

case $MENU in
1)
  echo "执行备份数据库"
  ./backup.sh
  ;;
2)
  echo "清理日志"
  ;;
3)
  echo "软件升级"
  ;;
4)
  echo "软件回滚"
  ;;
5)
  echo "删库跑路"
  ;;
*)
  echo "INPUT FALSE!"
esac

在这里插入图片描述

循环

将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件。

重复运行次数
  循环次数事先已知
  循环次数事先未知
  
常见的循环的命令: for, while, until

在这里插入图片描述

#Centos7的for帮助比Centos8的全面
[root@centos7 ~]# help for
for: for NAME [in WORDS ... ] ; do COMMANDS; done
    Execute commands for each member in a list.
    
    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.
    
    Exit Status:
    Returns the status of the last command executed.
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
    Arithmetic for loop.
    
    Equivalent to
    	(( EXP1 ))
    	while (( EXP2 )); do
    		COMMANDS
    		(( EXP3 ))
    	done
    EXP1, EXP2, and EXP3 are arithmetic expressions.  If any expression is
    omitted, it behaves as if it evaluates to 1.
    
    Exit Status:
    Returns the status of the last command executed.

for循环

格式1

do和done可以看成一对花括号{}, do是“{”开始,done是“}”结束,do和done中间包裹着循环体;

方式1
  for NAME [in WORDS ...] ; do COMMANDS; done
  # do和done可以看成一对花括号{}, do是{开始,done是}结束,do和done中间包裹着循环体;

方式2
  for 变量名 in 列表; do
      循环体
  done

方式3
  for 变量名 in 列表
  do
      循环体
  done
for i in 1 2 3 4 5 6 ; do echo i=$i; done

在这里插入图片描述

执行机制:
  ·依次将列表中的元素赋值给"变量名";每次赋值后即执行一次循环体;直到列表中的元素耗尽,循环结束
  ·如果省略[in WORDS...],此时使用位置参数变量 in "$@"
for循环列表生成方式:
  1.直接给出列表
  2.整数列表:
     {start..end}
     $(seq [start [step]] end)
     
  3.返回列表的命令:
    $(COMMAND)
  
  4.使用glob, 如: *.sh
  5.变量引用, 如:$@, $*, $#

范例:面试题,计算1+2+3+…+100的结果

sum=0; for i in {1..100}; do let sum+=i;done ; echo sum=$sum
seq -s+ 100 |bc
echo {1..100}|tr ' ' +|bc
seq 100|paste -sd +|bc

在这里插入图片描述

九九乘法表

一步步来,先打印一行

#! /bin/bash
for i in {1..9};do
    echo -n "${i}x9=$((i*9))  ";
done
echo

在这里插入图片描述
再打印列

#! /bin/bash
for i in {1..9};do
   for j in `seq $i`;do
       echo -e "${i}x${j}=$((i*j))\t\c ";
   done
   echo
done

在这里插入图片描述

生产题,修改所有文件的后缀名

生产案例:将指定目录下的文件所有文件的后缀改名为bak后缀

怎么区文件的前缀和后缀?
在这里插入图片描述

echo leilei.jpg | sed -En 's/^(.*)\.([^.]+)$/\2/p'

在这里插入图片描述
vim for_rename.sh

#! /bin/bash
DIR=/data/test
cd $DIR || { echo 无法进入 $DIR; exit 1; }
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

在这里插入图片描述

面试题,移动所有目录

M37期面试题,要求将目录YYYY-MM-DD/中所有文件,移动到YYYY-MM/DD/下

1.创建YYYY-MM-DD格式的目录,当前日期1年 365天到目前共365个目录,里面有10个文件.log后缀的文件

[root@centos7 ~]# date
Thu Aug  8 18:52:57 CST 2024
[root@centos7 ~]# date -d '' +%F
2024-08-08
[root@centos7 ~]# date -d '-1 day' +%F
2024-08-07
[root@centos7 ~]# date -d '+1 month' +%F
2024-09-08
[root@centos7 ~]# date -d '-1 year' +%F
2023-08-08
[root@centos7 ~]# 
#! /bin/bash
PDIR=/data/test
for i in {1..356};do
    DIR=`date -d "-$i day" +%F`
    mkdir -pv $PDIR/$DIR
    cd $PDIR/$DIR
    for j in {1..10};do
        touch $RANDOM.log
    done
done

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.将上面的目录移动到YYYY-MM/DD/下

vim move_dir.sh

#! /bin/bash
DIR=/data/test
cd $DIR || { echo 无法进入 $DIR; exit 1; }
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/*-*-*

在这里插入图片描述
在这里插入图片描述

面试题:扫描一个网段,判断主机的状态,打印出在线的主机。
#! /bin/bash
NET=192.168.10
for ID in {1..254};do
    {
      ping -c1 -w1 $NET.$ID &> /dev/null && echo $NET.$ID is up || echo $NET.$ID is down
    }&
done
wait

在这里插入图片描述

格式2

双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格的变量操作.也像Java的风格。

for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
for (( 控制变量初始化;条件判断表达式;控制变量的修正表达式)); 
do
  循环体; 
done
计算从1加到100的和

vim sum.sh

#! /bin/bash
for ((sum=0,i=1;i<=100;i++));do
     let sum+=i;
done
echo sum=$sum

在这里插入图片描述

九九乘法表

先循环9行试试

#! /bin/bash
for((i=1;i<=9;i++));do
    echo "${i}X9=$((i*9))"
done

在这里插入图片描述
再循环9列试试

#!/bin/bash
for((i=1;i<=9;i++));do
    for((j=1;j<=i;j++));do
    echo "${j}X${i}=$((i*j))"
    done
    echo
done

在这里插入图片描述

#!/bin/bash  
for((i=1;i<=9;i++));do
    for((j=1;j<=i;j++));do
    echo -e "${j}X${i}=$((i*j))\c"
    done
    echo
done

在这里插入图片描述

#!/bin/bash  
for((i=1;i<=9;i++));do
    for((j=1;j<=i;j++));do
    echo -e "${j}X${i}=$((i*j))\t\c"
    done
    echo
done

在这里插入图片描述

while循环

[root@rocky ~]# help while
while: while COMMANDS; do COMMANDS; done
    Execute commands as long as a test succeeds.
    
    Expand and execute COMMANDS as long as the final command in the
    `while' COMMANDS has an exit status of zero.
    
    Exit Status:
    Returns the status of the last command executed.

格式

while COMMANDS; do COMMANDS; done

while COMMANDS; do 
   循环体; 
done

说明:
  CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;
  条件为"true",则执行一次循环;直到条件测试状态为"false"终止循环,
  因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正
  
  进入条件:CONDITION为true
  退出条件:CONDITION为false

无限循环

while true; do
   循环体
done

while : ; do
   循环体
done

范例:

求从1加到100的和
#!/bin/bash
sum=0
i=1
while (($i<=100));do
      sum=$((sum +i))
      i=$((i+1))
done
echo $sum

在这里插入图片描述

监控分区磁盘使用率

扫描"/dev/nv*"开头的磁盘分区,使用率达到15%时就报警输出实际占用率,脚本这里是每10秒就打印输出一次

#!/bin/bash
WARNING=15
while true;do
    df | sed -rn '/^\/dev\/nv/s#.* ([0-9]+)%.*#\1#p' | while read use ;do
        if [ $use -gt $WARNING ];then
            echo $use 
        fi
    done
    sleep 10
done

在这里插入图片描述

until循环

和while差不多,循环条件有差异

[root@rocky ~]# help until
until: until COMMANDS; do COMMANDS; done
    Execute commands as long as a test does not succeed.
    
    Expand and execute COMMANDS as long as the final command in the
    `until' COMMANDS has an exit status which is not zero.
    
    Exit Status:
    Returns the status of the last command executed.

格式

until: until COMMANDS; do COMMANDS; done

until: until COMMANDS; do
   循环体;
done

说明:
  进入条件:CONDITION为false
  退出条件:CONDITION为true

无限循环

until false; do
  循环体
Done
#! /bin/bash
sum=0
i=1
until (($i>100));do            看清楚哦$i>100,是大于100哦
      sum=$((sum +i))
      i=$((i+1))
done
echo $sum

在这里插入图片描述

循环控制语句

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

continue

[root@rocky ~]# help continue
continue: continue [n]
    Resume for, while, or until loops.
    
    Resumes the next iteration of the enclosing FOR, WHILE or UNTIL loop.
    If N is specified, resumes the Nth enclosing loop.
    
    Exit Status:
    The exit status is 0 unless N is not greater than or equal to 1.

格式:

while CONDITION1; do
    CMD1
    ...
    if CONDITION2 ; then
         continue
    fi
    CMDn
    ...
done

continue 1:结束内层

#!/bin/bash
for((i=0;i<10;i++));do
   for((j=0;j<10;j++));do
           [ $j -eq 5 ] && continue 1
           echo $j
   done
   echo "----------------"
done

在这里插入图片描述
continue 2:结束外层

#!/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

在这里插入图片描述

break

break [N]:提前结束第N层整个循环,最内层为第1层

[root@rocky ~]# help break
break: break [n]
    Exit for, while, or until loops.
    
    Exit a FOR, WHILE or UNTIL loop.  If N is specified, break N enclosing
    loops.
    
    Exit Status:
    The exit status is 0 unless N is not greater than or equal to 1.

格式:

while CONDITION1; do
   CMD1
   ...
   if CONDITION2; then
       break
   fi
   CMDn
   ...
done

break 1:内层循环

#!/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

在这里插入图片描述
break 2:结束这个循环

#!/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

在这里插入图片描述

break结束点菜结账
#!/bin/bash
sum=0
COLOR='echo -e \033[1; 31m'
COLOR2='echo -e \033[1; 32m'
END="\033[0m"

while true ;do
    echo -e "\033[33;1m\c"
    cat <<-EOF
1)鲍鱼
2)满汉全席
3)龙虾
4)燕窝
5)帝王蟹
6)点菜结束,结帐
    EOF
    echo -e "\033[0m"

    read -p "请点菜(1-6): "  MENU
   case $MENU in
   1|4)
      $COLOR'菜价: $10'$END
      let sum+=10
      ;;
   3|5)
      $COLOR'菜价:$20'$END
      let sum+=20
      ;;
   2)
      $COLOR'菜价:$200000'$END
      let sum+=200000
      ;;
   6)
      $COLOR2"你点的菜总价格是人\$$sum" $END
      break
      ;;
   *)
      echo "点错了,没有这道菜"
      ;;
    esac
    $COLOR2"你点的菜总价格是 \$$sum" $END
done

shift命令

shift [n] 用于将参量列表list左移指定次数,缺省为左移一次。
参量列表list一旦被移动,最左端的那个参数就从列表中删除。while循环遍历位置参量列表时,常用到shift

[root@rocky scripts]# help shift
shift: shift [n]
    Shift positional parameters.
    
    Rename the positional parameters $N+1,$N+2 ... to $1,$2 ...  If N is
    not given, it is assumed to be 1.
    
    Exit Status:
    Returns success unless N is negative or greater than $#.

循环与菜单select

[root@rocky scripts]# help select
select: select NAME [in WORDS ... ;] do COMMANDS; done
    Select words from a list and execute commands.
    
    The WORDS are expanded, generating a list of words.  
    The set of expanded words is printed on the standard error, eachpreceded by a number. 
    If `in WORDS' is not present, `in "$@"'is assumed.  
    The PS3 prompt is then displayed and a line read from the standard input.  
    If the line consists of the number corresponding to one of the displayed words, 
    then NAME is set to that word.  If the line is empty, WORDS and the prompt are redisplayed.       
    If EOF is read, the command completes.  Any other value read causes NAME to be set to null.     
    The line read is saved in the variable REPLY.  
    COMMANDS are executed after each selection until a break command is executed.
    
    Exit Status:
    Returns the status of the last command executed.

格式

select NAME [in WORDS ... ;] do COMMANDS; done

select NAME in list ; do 
    循环体命令; 
done

说明

 1.select循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示PS3提示符,等待用户输入
 2.用户输入菜单列表中的某个数字,执行相应的命令
 3.用户输入菜单列表中的某个数字,会将对应的WORD值赋值给NAME变量
 4.用户输入被保存在内置变量REPLY5.select是个无限循环,因此要用break 命令退出循环,或用exit 命令终止脚本。也可以按ctrl+c退出循环
 6.select经常和case联合使用
 7.与for循环类似,可以省略in list,此时使用位置参量

在这里插入图片描述

select点菜
#!/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"

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值