shell脚本编程之路3

2022-09-28 20:32:59 浏览数 (1)

[TOC]

0x07 shell编程选择分支

描述:我们学过的每一种高级编程语言,都在存在流程/分支选择/循环等结构,同样shell编程中也有这是与windows上的bat编程不一样的点;shell选择分支嵌套建议不超过三层,为了执行的效率和其他人容易读懂;

if 语句

if 语句通过关系运算符判断表达式的真假来决定执行哪个分支,Shell中有三种 if … else 语句表示结构:

代码语言:javascript复制
#1.单分支语句
if ...;then ... fi 语句;
if ...;then ... else ... fi 语句;

#2.双分支语句
if ...;then ...  elif ...;then ... else ... fi 语句

(1)单分支语句

代码语言:javascript复制
#1.语法结构 if......fi语句
#如果 expression 返回 true,then 后边的语句将会被执行;如果返回 false,不会执行任何语句。
if [ expression ]
then
   Statement(s) to be executed if expression is true
fi #最后必须以 fi 来结尾闭合 if,fi 就是 if 倒过来拼写,后面也会遇见。

#即
if [表达式];then
    语句
fi


#2.语法结构  if ... else ... fi 语句
# 如果 expression 返回 true,那么 then 后边的语句将会被执行;否则,执行 else 后边的语句
if [ expression ]
then
   Statement(s) to be executed if expression is true
else
   Statement(s) to be executed if expression is not true
fi

WeiyiGeek.if..fi

WeiyiGeek.if..else..fi

(3)多分支语句

代码语言:javascript复制
#3.语法结构  if ... elif ...else... fi 语句
#可以对多个条件进行判断,注意的是每个条件表达式后面都有一个then,语法为: 
#依次判断,为真则结束判断,为Fasle则继续向下判断.
if [ expression 1 ];then
   Statement(s) to be executed if expression 1 is true
elif [ expression 2 ];then
   Statement(s) to be executed if expression 2 is true
elif [ expression 3 ];then
   Statement(s) to be executed if expression 3 is true
else
   Statement(s) to be executed if no expression is true
fi
#哪一个 expression 的值为 true,就执行哪个 expression 后面的语句;如果都为 false,那么不执行任何语句

WeiyiGeek.if..elif

实际案例:

代码语言:javascript复制
#!/usr/bin/env bash
read -p "Please INput A:" a
read -p "Please INput B:" b
read -p "Please INput C:" c	

echo -e "ne[1;32m  ##示例1.单分支语句 e[0m"
#注意:条件表达式要放在方括号之间,并且要有空格,例如 [$a==$b] 是错误的,必须写成 [ $a == $b ]
if [ $a == $b ];then     #注意[ 里面的关系运算符要用空格隔开 ]
    echo "a is equal to b!!" 
fi
if [ $a != $b ];then
     echo "a is not equal to b!!"
fi
#if语句也可以写成一行,以命令的方式来运行,像这样: 
if test $[2*3] -eq $[1 5]; then echo 'The two numbers are equal!'; fi;


echo -e "n##示例2.单分支语句"
#判断从标准输入的变量b和c的值是否相等
if [ $c == $b ];then
  echo "a is equal to b!!"
else
  echo "a is not equal to b!!"
fi

#判断iptables是否在运行,如果已经在运行提示信息,如果没有开启它。
service iptables status &> /dev/null
if [ $? -eq 0 ];
  then
    echo "iptables service is running"
  else
    service iptables restart
fi


echo -e "n##示例3.多分支语句" 
#注意elif使用中没一个条件后都有一个then关键字;
a=10;b=20
if [ $a == $b ];then
       echo "a is equal to b"
    elif [ $a -gt $b ];then  # >
       echo "a is greater than b"
    elif [ $a -lt $b ];then # <
       echo "a is less than b"
    else
       echo "None of the condition met"
fi

WeiyiGeek.if示例

if…else 语句也经常与 test 命令或者 [ ] 结合使用如下所示:

代码语言:javascript复制
#!/usr/bin/env bash
#if与test命令和[]以及[[]]联合使用

#示例1.test
num1=$[2*3];num2=$[1 5]
if test $[num1] -eq $[num2]
then
    echo 'The two numbers are equal!'
else
    echo 'The two numbers are not equal!'
fi
#The two numbers are equal!


#示例2.[]
read -p "测试输入:" number
if [ ! -z "$number" -a "$number" == "1024" ];then  #注意这里变量外面的双引号(非常重要)
    echo "输出正确:1024"
else
    echo "您不满足条件!"
fi
#输出正确:1024


#示例3.[[]]
a=$(env|grep "USER"|cut -d "=" -f2)
if [[ $a == "root" ]];then
    echo "Current User is Root"
elif [[ ! -z $a ]];then
    echo "Current User is ${a}"
fi
#Current User is ubuntu

高级示例:

代码语言:javascript复制
#!/bi/bash
#示例1.判断分区得使用率,建议if中表达式使用括号围起来
rate=$(df -h | grep "rootfs" | awk '{print $5}' | cut -d "%" -f1)
if (test $rate -ge 10 );then
  echo "Warning /dev/sda1 is full ${rate}"  
else
  echo "/dev/sda1 is more free${rate}"
fi
#Warning /dev/sda1 is full 19


#示例2.判断一个是不是一个目录,变量必须采用""进行包含
read -t 30 -p "请输入一个文件路径或者目录路径:" fdpath
if (test -f "$fdpath");then
    echo "它是一个文件,路径是:$fdpath"
elif (test -d "$fdpath");then
    echo "它是一个目录,路径是:$fdpath"
else
    echo "既不是目录也不是文件!"
fi
#它是一个文件,路径是:/tmp/test.txt


#示例3.判断apache服务得shell脚本名字不能有httpd关键字
test=$(ps aux | grep httpd | grep -v httpd) #截取http进程并且排除该项命令
if [[ -n "$test" ]];then
    #在不为空的情况下执行 -z : 是不是为零(是则真)
    echo  "$(date) httpd is ok!" >> /tmp/httpslogs.logs
else 
    #否则重新启动httpd
    /etc/rc.d/init.d/httpd stop &> /dev/null
    /etc/rc.d/init.d/httpd start &> /dev/null
    echo "$(date) restart httpd !!" >> /tmp/httpderror.logs
fi

WeiyiGeek.if常规运算

case 语句

描述:case … esac 与其他语言中的 switch … case 语句类似,也是一种多分支选择结构。 case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。

语句格式语法:

代码语言:javascript复制
case var值 in
"值1")
    command1 #如果等于值1则执行
    command2
    ;;
"值2")
    command1 
    command2
    ;;
*)
    command1 #如果变量值都不是以上值,则执行此程序;
    command2
    ;;
esac
``` 
case工作方式如上所示:取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为`变量或常数`,匹配发现取值符合某一模式后,其间所有命令`开始执行直至 ;;` ,取值将检测匹配的每一个模式:一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。
- 如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令
- ;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后

![WeiyiGeek.case语句](https://cdn.jsdelivr.net/gh/WeiyiGeek/blogimage/2019/20190708170556.png)

实际案例:
```bash
#!/bin/bash
#示例1. 下面的脚本提示输入1到4,与每一种模式进行匹配
echo 'Input a number between 1 to 4'
echo 'Your number is:c'
read aNum
case $aNum in
        1)  echo 'You select 1'
        ;;
        2)  echo 'You select 2'
        ;;
        3)  echo 'You select 3'
        ;;
        4)  echo 'You select 4'
        ;;
        *)  echo 'You do not select a number between 1 to 4'
        ;;
esac


#示例2:比较新颖的思路
option="${1}"
case ${option} in
       -f|-F) 
        FILE="${2}"
        echo "File name is $FILE"
        ;;
       -d|-D) 
        DIR="${2}"
        echo "Dir name is $DIR"
        ;;
        #还可以这样
        [yY]|[yY][eE][sS])
        echo "YES"   
        ;;
        [nN]|[nN][oO]
        echov "NO"
        ;;
       *) 
        echo "`basename ${0}`:usage: [-f file] | [-d directory]"
        exit 1 # Command to come out of the program with status 1
        ;;
esac

WeiyiGeek.case语句示例

注意事项:

  • expression 和方括号([ ])之间必须有空格,否则会有语法错误
  • 可以使用三种方式让while循环读文件,但是并不建议采用while而是通过awk可以更快;

0x08 shell编程循环退出
for 语句

描述:与其他编程语言类似,Shell支持for循环列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔,每循环一次,就将列表中的下一个值赋给变量。

for语句语法格式:

代码语言:javascript复制
##方法1:##
#in 列表是可选的,如果不用它 for 循环使用命令行的位置参数
for 变量 in 取值列表;do
    command1
    command2
    ...
    commandN
done

##方法2##
for((初始值;循环条件;变量控制))
do
   commond
done

WeiyiGeek.for语句

for循环中用seq产生循环次数,加上C语言形式的for循环语句; 对于固定次数的循环,可以通过seq命令来实现,就不需要变量的自增了,这里的C语言for循环风格是挺熟悉的吧。

实际案例:

代码语言:javascript复制
#!/bin/bash
#示例1.循环示例
for loop in 1 2 3;do
    echo "(1)The value is: $loop"
done

for loop in $(seq 0 2);do
    echo "(2)The value is: $loop"
done 

for i in {0..2} #输出{1..2}列表里面的数字,等同于seq 1 2
do
   echo $i
done

#执行结果:
(1)The value is: 1
(1)The value is: 2
(1)The value is: 3
(2)The value is: 0
(2)The value is: 1
(2)The value is: 2


#示例2.对目录中的文件做for循环 
#如果只引用当前工作目录中的文件(例如如果输入for x in *),则产生的文件列表将没有路径信息的前缀(可以不用basename)
for x in /var/log/*
do
  #这个$x获得的是绝对路径文件名,可以使用basename可执行程序来除去前面的路径信息
   echo $(basename $x) is a file living in /var/log
done 

  # 执行结果:
  # alternatives.log is a file living in /var/log
  # apt is a file living in /var/log
  # btmp is a file living in /var/log

# 批量压缩有特点格式的日志文件:
for i in $(ls info-20210427.log.{0..5});do tar -zcvf ${i}.tar.gz ${i};done
rm -rf info-20210427.log.{0..5}

# 示例3.批量解压包 
ls *.tar.gz >> gz.log 2>/dev/null
ls *.tgz >> gz.log 2>/dev/null
echo "正在解压缩 : Wait,Please!!"
for i in $(cat gz.log)  #【这里值得学习在写脚本的时候非常有用】
    do
      tar -zxf $i &> /dev/null
    done
rm -rf gz.log


# 示例4.利用计算1到100相加
sum=0
for((i=0;i<=100;i  ))
do
    sum=$[ $sum   $i ]
done
echo "一到一百相加得:$sum" #5050


# 示例5.快速时间同步
for i in {78..83};do echo -n "$i -";ssh -p 20211 root@192.168.1.${i} 'ntpdate 192.168.10.254 && date';done

# 示例6.切割文件中自定字符并根据所切割的列进行排序去重,然后将得到的结果进行创建目录
for dir in $(cut -d " " -f 3 copy.sh  | cut -d "/" -f 4 | sort | uniq );do  mkdir -vp ${dir}; done

高级shell编程: passwd默认是要用终端作为标准输入,加上–stdin表示可以用任意文件做标准输入 于是这里用管道作为标准输入,且直接传人密码,不用重复输入.

代码语言:javascript复制
# 批量建立用户
echo 123@4@56wn | /usr/bin/passwd test
echo 123@4@56wn | /usr/bin/passwd --stdin test 
# --stdin 将echo输出得值接收赋予 test 用户   
--stdin 从标准输入读取令牌(只有根用户才能进行此操作)

WeiyiGeek.建立用户

同样我们可以批量删除用户

代码语言:javascript复制
# less /etc/passwd | grep "/bin/bash" | grep -v root | cut -d ":" -f1 
Srcweb
postgres
mysql
git
test

WeiyiGeek.删除用户

while 语句

描述:while 循环用于不断执行一系列命令(为真执行),也用于从输入文件中读取数据;命令通常为测试条件。

基础语法:

代码语言:javascript复制
#命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假
while [ 条件表达式]    #条件成立会一直执行
do
   Statement(s) to be executed if command is true
done

WeiyiGeek.while语句

实际案例:

代码语言:javascript复制
#示例1.基本的while循环,测试条件是:
#如果COUNTER小于5,那么返回 true,COUNTER从0开始,每次循环处理时,COUNTER加1。运行上述脚本返回数字1到5然后终止
COUNTER=0
while [[ $COUNTER -lt 5 ]]
do
    echo $((COUNTER  )) > /dev/null    # 相当于在循环内自加-在i  
    echo -n "$COUNTER  "
done
#执行结果
1  2  3  4  5 


#示例2.从1加到100的while计算方式.
i=1;s=0
while [[ $i -le 100 ]];do
    s=$(( $s $i ))  #变量赋值不用带$ 变量
    i=$(( $i 1 ))
done
echo "1 2 ... 10 sum is: ${s}" #1 2 ... 10 sum is: 5050


#示例3.while循环可用于读取键盘信息,思路毕竟新新颖.
#下面的例子中,输入信息被设置为变量FILM,按<Ctrl-D>结束循环
echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film: '
while read FILM
do
   echo "Yeah! great film the $FILM"
done

# 示例4.批量转换png后缀的图片为webp图片。
for i in /tmp/*.png; do echo "${i/.png/.webp}"; cwebp -q 80 ${i} -o ${i/.png/.webp}; done
/tmp/wechat-search.png
/tmp/wechat-search.webp

WeiyiGeek.while循环监听输入

until 语句

描述:until 循环用于不断执行一系列命令(为真停止),也用于从输入文件中读取数据;命令通常为测试条件。 Tips:一般while循环优于until循环,但在某些时候,也只是极少数情况下,until 循环更加有用。

  • until 循环执行一系列命令直至条件为 true 时停止。
  • until 循环与 while 循环在处理方式上刚好相反。

基础语法:

代码语言:javascript复制
# command 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环
until [ 表达式 ] 
do
   Statement(s) to be executed until command is true
done

实际案例:

代码语言:javascript复制
# 例1:使用 until 命令输出 0 ~ 9 的数字: 
#!/bin/bash
a=0
until [ ! $a -lt 10 ]  # a小于10取反->a大于10为假,则执行
do
   echo $a
   a=`expr $a   1`
done


#示例2. 从1加到100的until计算方式.
i=1;s=0
until [[ $i -gt 100 ]];do
    s=$(( $s $i ))
    i=$(( $i 1 ))
done
echo "The sum is: ${s}" #The sum is: 5050

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,像大多数编程语言一样,Shell也使用 break 和 continue 来跳出循环。

break语句

描述:在for、while、until等循环语句中,用于跳出当前所在的循环体,执行循环体之后的语句。

基础语法:

代码语言:javascript复制
#示例1:
while [[ express ]];do
    commands
    #通常在循环体中与条件语句一起使用
    if [ express ];then
        break;
    fi
done

#示例2. 在嵌套循环中,break 命令后面还可以跟一个整数,表示跳出第几层循环。
#例如: 表示跳出第 n 层循环
break n

实际案例:

代码语言:javascript复制
#示例1.脚本进入死循环直至用户输入数字大于5要跳出这个循环,返回到shell提示符下,就要使用break命令。 
while : #死循环  (注意这里的 : 也是一条命令,无论怎么样返回都为真且exit状态为0)
do
    echo -n "Input a number between 1 to 5: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "Your number is $aNum!"
        ;;
        *) echo "You do not select a number between 1 to 5, game is over!"
            break
        ;;
    esac
done


#示例2.嵌套循环的例子,如果 var1 等于 2,并且 var2 等于 0,就跳出2层循环: 
for var1 in 1 2 3
do
   for var2 in 0 5
   do
      if [ $var1 -eq 2 -a $var2 -eq 0 ]  #当var1=2 与 var=0时跳出循环
      then
         break 2
      else
         echo "$var1 $var2"
      fi
   done
done

#运行结果
1 0 
1 5

WeiyiGeek.示例1

continue 语句

描述:在for、while、until等循环语句中,用于跳过循环体内余下的语句,重新判断条件以便执行下一次循环; 同样continue 后面也可以跟一个数字,表示跳出第几层循环继续下一次循环。

语法示例:

代码语言:javascript复制
while :
do
    commands
    if [[ express ]];then
        #跳出当前循环,重新进入下一次循环(与break一样通常与条件语句一起使用)
        continue
    fi
    commands
done

实际案例:

代码语言:javascript复制
#示例1.当输入的数字是1~5则退出while循环,否则继续执行 *) 中的命令并且永远也不会输出 Game is over
while :
do
    echo -n "Input a number between 1 to 5: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "Your number is $aNum!"
           break
        ;;
        *) echo "You do not select a number between 1 to 5!"
            continue
            echo "Game is over!"
        ;;
    esac
done

运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句echo “Game is over!” # 永远不会被执行

0 人点赞