六、 循环结构和分支结构

 

1.for循环结构

 

 

列表式循环

 

 列表for循环:用于将一组命令执行**已知的次数**

根据变量的不同取值,重复执行不同序列

 

for语法格式
for 变量名 in 变量值列表;
do 
    当变量值列表有值赋给变量名时执行的语句块             
done

 

或者:

for variable in {list}
do
     command 
     command
     …
done
或者
for variable in a b c
do
    command
    command
done

 

举例说明

 

# for var in {1..10};do echo $var;done
# for var in 1 2 3 4 5;do echo $var;done
# for var in `seq 10`;do echo $var;done
# for var in $(seq 10);do echo $var;done
# for var in {0..10..2};do echo $var;done
# for var in {2..10..2};do echo $var;done
# for var in {10..1};do echo $var;done
# for var in {10..1..-2};do echo $var;done
# for var in `seq 10 -2 1`;do echo $var;done

 

 

例子1:

#!/bin/bash
#for i in 1 2 3 4 5 6 7 8 9 10 
#for i in `head /etc/passwd`
for i in `ls /shell/*.sh`
do 
   echo $i
done

 

192.168.1.1~192.168.1.5主机的ip地址输出到屏幕上

 

#!/bin/bash
mynet=192168.1.
#for i in 1 2 3 4 5
#for i in `seq 254`
#for i in `30 45`
#for i in `seq 10 2 30`
#for i in {a..z}
for i in {1..10}                     
do 
      echo $mynet$i
done

 

 

 

例子2:

#!/bin/bash
for i in `seq 10`
do 
   if [ `expr $i % 2` -eq 0  ];then
       echo $i
   fi
done

 

用for循环输出192.168.1.0/24网段内所有主机的ip地址

#!/bin/bash
for I in `seq 1 254`
do echo 192.168.1.$I
done

 

 

把自己不能与其通信的本网段的主机的ip地址输出到屏幕上
并统计不能与其通信的主机的个数

#!/bin/bash
j=0
mynet=192.168.1
for i  in `seq 5`
do
  ping -c 3 $mynet.$i &> /dev/null
  if [ $? -ne 0 ];then
      echo "$mynet.$i"
      let j++
  fi
done
echo "不在的主机有 $j 台"

 

例子3:

#!/bin/bash
mynet=192.168.1
for i  in `seq 5`
do
 (ping -c 3 $mynet.$i &> /dev/null
  if [ $? -ne 0 ];then
      echo "主机 $mynet.$i not line!!!" 
  fi) &
done

 

注意:

() 把当前的程序放到子shell里执行
 &  把程序置入后台运行

 

把1-20之间的偶数输出 并统计奇数的个数。

 

#!/bin/bash
for s in `seq 20`

do
  if [ `expr $s % 2` -eq 0 ];then
     echo $s
     let j++
  fi 
done
echo "共有$j个奇数"

 

把系统已有的binlog日志文件输出

方法1

 


#!/bin/bash
mysqldir=/var/lib/mysql
cd $mysqldir
for i in  `cat localhost-bin.index`
do
   binlogname=`basename $i`
   echo $binlogname
done

 

方法2:

#!/bin/bash
for binlog in `cat /var/lib/mysql/data-bin.index`
do
    echo $binlog
done

 

方法3:

#!/bin/bash
for binlog in `cat /var/lib/mysql/data-bin.index`
do
   
    binlogname=`basename $binlog`
    echo $binlogname
done      

 

 

每天的 23:30 备份当前数据库服务器上新生成的binlog日志文件到系统的/newbinlogdir目录里. (当前正在使用binlog日志文件不备份)

 

#!/bin/bash
datadir=/var/lib/mysql
bakdir=/mysqlbak
lastbinlogname=`tail -1 $datadir/mysqld-bin.index  | awk  -F"/"  '{print $2}'`

if [ -e $bakdir ];then
    cd $datadir
    for i  in  `cat  mysqld-bin.index `
    do
        binlogname=`basename $i`
        if [ "$binlogname" != "$lastbinlogname" ];then
            if [ ! -e $bakdir/$binlogname ];then
                cp  $binlogname  $bakdir
            else
                echo "$binlogname baked"     
            fi
        else
            echo "$lastbinlogname useeing!!!!"
        fi

    done
else
    mkdir $bakdir
    cd $datadir
    for i  in  `cat  mysqld-bin.index `
    do
        binlogname=`basename $i`
        if [ "$binlogname" != "$lastbinlogname" ];then
            if [ ! -e $bakdir/$binlogname ];then
                cp  $binlogname  $bakdir
            else
                echo "$binlogname baked"     
            fi
        else
            echo "$lastbinlogname useeing!!!!"
        fi
    done
fi

 

 

㈡ 不带列表循环

 

不带列表的for循环执行时由用户指定参数和参数的个数

 

基本语法格式

 

for variable
do
    command 
    command
    …
done

 

 

举例说明

 

#!/bin/bash
for var
do
echo $var
done

echo "脚本后面有$#个参数"

 

 

 

(三)类C语言风格的for循环

 

基本语法结构


 

for(( expr1;expr2;expr3 ))
do
    command
    command
    …
done

 

 

说明:

expr1:定义变量并赋初值
expr2:决定是否进行循环(条件);即条件判断
expr3:决定循环变量如何改变,决定循环什么时候退出;即步长

 例子:

for (( i=1;i<=5;i++))
do
    echo $i
done

 

 

举例说明

 

 

 # for ((i=1;i<=5;i++));do echo $i;done
 # for ((i=1;i<=10;i+=2));do echo $i;done
 # for ((i=2;i<=10;i+=2));do echo $i;done

 

 

例子:

for((i=5;i==3;i++))
do
   echo $i
   sleep 1
done

 

使用for循环嵌套实现九九乘法表

 

#!/bin/bash
#通过for循环嵌套实现九九乘法表,其中temp用于保存i+j的值
for (( i = 1;i <=9; i++ ))
do
    #內层循环
    for (( j=1; j <=i; j++ ))
    do
        let "temp = i * j"   #暂时存储i*j的值
        echo -n "$i*$j=$temp "
    done
    echo ""
done

 

 

使用for循环嵌套实现8X8个的棋盘

 

#!/bin/bash
#外层循环
for (( i = 1;i <=8; i++ ))
do
  #內层循环
    for (( j=1; j <=8; j++ ))
    do
    #下面两行用于不重叠显示黑白格
        total=$(( $i + $j ))
        tmp=$(( $total % 2 )) 
        if [ $tmp -eq 0 ];
        then
            echo -e -n "\033[47m "   #显示白格
        else 
            echo -e -n "\033[40m "   #显示黑格
        fi
    done
    echo "" #换行
done

 

 

 

 

2. 应用案例

㈠ 脚本计算1-100奇数和

① 思路

 

    定义一个变量来保存奇数的和 sum=0
    找出1-100的奇数,保存到另一个变量里 i,i=遍历出来的奇数
    从1-100中找出奇数后,再相加,然后将和赋值给变量 循环变量 for
    遍历完毕后,将sum的值打印出来

 

 

② 落地实现(条条大路通罗马)

 

 

#!/bin/bash
#定义一个变量来保存奇数的和
sum=0
#打印1-100的奇数并且相加重新赋值给sum
for i in {1..100..2}
do
    sum=$[ $i + $sum ]
done
#打印1-100的奇数和
echo "1-100的奇数和为:$sum"

#!/bin/bash
#定义一个变量来保存奇数的和
sum=0
#打印1-100的奇数并且相加重新赋值给sum
for (( i=1;i<=100;i+=2))
do
    let sum=sum+$i
    或者
    let sum=sum+i
    或者
    let sum=$sum+$i
done
#打印1-100的奇数和
echo "1-100的奇数和为:$sum"


#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
    if [ $[$i%2] -ne 0 ];then
    let sum=sum+$i
    fi
done
echo "1-100的奇数和是:$sum"


#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
    [ $[$i%2] -eq 0 ] && true  || let sum=sum+$i
done
echo "1-100的奇数和是:$sum"


延伸:
true    真
:        真
false     假



方法1:
#!/bin/bash
sum=0
for i in {1..100..2}
do
    sum=$[$i+$sum]
done
echo "1-100的奇数和为:$sum"

方法2:
#!/bin/bash
sum=0
for ((i=1;i<=100;i+=2))
do
    let sum=$i+$sum
done
echo "1-100的奇数和为:$sum"

方法3:
#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
    if [ $[$i%2] -ne 0 ];then
    let sum=$sum+$i
    fi
或者
test $[$i%2] -ne 0 && let sum=$sum+$i

done
echo "1-100的奇数和为:$sum"

方法4:
sum=0
for ((i=1;i<=100;i++))
do
    if [ $[$i%2] -eq 0 ];then
    continue
    else
    let sum=$sum+$i
    fi
done
echo "1-100的奇数和为:$sum"

#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
    test $[$i%2] -eq 0 && continue || let sum=sum+$i
done
echo "1-100的奇数和是:$sum"

 

循环控制语句

循环体:do....done之间的内容

 

continue:继续;表示循环体内下面的代码不执行,重新开始下一次循环

break:打断;马上停止执行本次循环,执行循环体后面的代码

exit:表示直接跳出程序

 

[root@server ~]# cat for5.sh 
#!/bin/bash
for i in {1..5}
do
    test $i -eq 2 && break || touch /tmp/file$i
done
echo hello hahahah

 

㈡ 判断所输整数是否为质数

质数(素数):只能被1和它本身整除的数叫质数。 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

① 思路

 

    让用户输入一个数,保存到一个变量里 read -p "请输入一个正整数:" num
    如果能被其他数整除就不是质数——>$num%$i是否等于0 $i=2到​$num-1
    如果输入的数是1或者2取模根据上面判断又不符合,所以先排除1和2
    测试序列从2开始,输入的数是4——>得出结果$num不能和$i相等,并且$num不能小于$i

 

 

 

② 落地实现

 

#!/bin/env bash
#定义变量来保存用户所输入数字
read -p "请输入一个正整数字:" number

#先排除用户输入的数字1和2
[ $number -eq 1 ] && echo "$number不是质数" && exit
[ $number -eq 2 ] && echo "$number是质数" && exit

#循环判断用户所输入的数字是否质数

for i in `seq 2 $[$number-1]`
    do
     [ $[$number%$i] -eq 0 ] && echo "$number不是质数" && exit
    done
echo "$number是质数"

优化思路:没有必要全部产生2~$[$number-1]序列,只需要产生一半即可。

更好解决办法:类C风格完美避开了生成序列的坑
for (( i=2;i<=$[$number-1];i++))
do
        [ $[$number%$i] -eq 0 ] && echo "$number不是质数" && exit

done
echo "$number是质数"

 

㈢ 批量创建用户

**需求:**批量加5个新用户,以u1到u5命名,并统一加一个新组,组名为class,统一改密码为123

① 思路

 

    添加用户的命令 useradd -G class
    判断class组是否存在 grep -w ^class /etc/group 或者groupadd class
    根据题意,判断该脚本循环5次来添加用户 for
    给用户设置密码,应该放到循环体里面

 

 

② 落地实现

 

 

#!/bin/env bash
#判断class组是否存在
grep -w ^class /etc/group &>/dev/null
test $? -ne 0 && groupadd class

#循环创建用户
for ((i=1;i<=5;i++))
do
    useradd -G class u$i
    echo 123|passwd --stdin u$i
done




方法一:
#!/bin/bash
#判断class组是否存在
grep -w class /etc/group &>/dev/null
[ $? -ne 0 ] && groupadd class
#批量创建5个用户
for i in {1..5}
do
    useradd -G class u$i
    echo 123|passwd --stdin u$i
done

方法二:
#!/bin/bash
#判断class组是否存在
cut -d: -f1 /etc/group|grep -w class &>/dev/null
[ $? -ne 0 ] && groupadd class

#循环增加用户,循环次数5次,for循环,给用户设定密码
for ((i=1;i<=5;i++))
do
    useradd u$i -G class
    echo 123|passwd --stdin u$i
done


方法三:
#!/bin/bash
grep -w class /etc/group &>/dev/null
test $? -ne 0 && groupadd class
或者
groupadd class &>/dev/null

for ((i=1;i<=5;i++))
do
useradd -G class u$i && echo 123|passwd --stdin u$i
done

 

 

3. 课堂练习

㈠ 批量创建用户

**需求1:**批量新建5个用户stu1~stu5,要求这几个用户的家目录都在/rhome.

 

#!/bin/bash
#判断/rhome是否存在
[ -f /rhome ] && mv /rhome /rhome.bak
test ! -d /rhome && mkdir /rhome
或者
[ -f /rhome ] && mv /rhome /rhome.bak || [ ! -d /rhome ] && mkdir /rhome 

#创建用户,循环5次
for ((i=1;i<=5;i++))
do
    useradd -d /rhome/stu$i stu$i
    echo 123|passwd --stdin stu$i
done

 

㈡ 局域网内脚本检查主机网络通讯

需求2:

写一个脚本,局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里

以10.1.1.1~10.1.1.10为例

10.1.1.1~10.1.1.254

#!/bin/bash
#定义变量
ip=10.1.1
#循环去ping主机的IP
for ((i=1;i<=10;i++))
do
    ping -c1 $ip.$i &>/dev/null
    if [ $? -eq 0 ];then
        echo "$ip.$i is ok" >> /tmp/ip_up.txt
    else
        echo "$ip.$i is down" >> /tmp/ip_down.txt
    fi
    或者
    [ $? -eq 0 ] && echo "$ip.$i is ok" >> /tmp/ip_up.txt || echo "$ip.$i is down" >> /tmp/ip_down.txt
done

[root@server shell03]# time ./ping.sh         

real    0m24.129s
user    0m0.006s
sys     0m0.005s

 

延伸扩展:shell脚本并发

 

并行执行:
{程序}&表示将程序放到后台并行执行,如果需要等待程序执行完毕再进行下面内容,需要加wait

#!/bin/bash
#定义变量
ip=10.1.1
#循环去ping主机的IP
for ((i=1;i<=10;i++))
do
{

        ping -c1 $ip.$i &>/dev/null
        if [ $? -eq 0 ];then
                echo "$ip.$i is ok" >> /tmp/ip_up.txt
        else
                echo "$ip.$i is down" >> /tmp/ip_down.txt
        fi
}&
done
wait
echo "ip is ok...."

[root@server ~]# time ./ping.sh 
ip is ok...

real    0m3.091s
user    0m0.001s
sys     0m0.008s

 

 

㈢ 判断闰年

需求3:

输入一个年份,判断是否是润年(能被4整除但不能被100整除,或能被400整除的年份即为闰年)

 

#!/bin/bash
read -p "Please input year:(2017)" year
if [ $[$year%4] -eq 0 -a $[$year%100] -ne 0 ];then
    echo "$year is leap year"
elif [ $[$year%400] -eq 0 ];then
    echo "$year is leap year"
else
    echo "$year is not leap year"
fi

 


 

 

while循环

 

while中的动作有可能一次都不执行

 特点:条件为真就进入循环;条件为假就退出循环

 

while语法

 

while 条件测试
do
   命令序列
done

 

或者:

while 表达式
    do
        command...
    done
    
while  [ 1 -eq 1 ] 或者 (( 1 > 2 ))
  do
     command
     command
     ...
 done

 

 

 

若条件表达式为真,重复执行动作,直到条件表达式为假才结束

 

 

do while

do while将动作前置,先执行前置,先执行动作,再判断条件表达式。因此do while中的动作至少执行一次。

 

 

do
  动作  
while (条件表达式)

 

 

使用while循环输出数字110(升序输出)降序输出

 

[root@localhost shell]# cat while10.sh
#!/bin/bash
j=1
while [ $j -le 10 ]
do 
   echo $j
    let j++ #j=j+1
done

 

 

 

批量添加10个系统用户要求如下:

1、用户的密码与用户名相同

2、用户的前缀是user

3、强制新添加的用户首登录系统时,重置自己的登录系统的密码。

4、编写脚本userdel.sh (删除刚才批量添加的10个系统用户)

 

 

 

 计算100以内所有正整数的和

 

#!/bin/bash
declare -i I=1
declare -i SUM=0

while [ $I -le 100 ]; do
  let SUM+=$I
  let I++
done
echo $SUM

 

 

 

 

 练习转换用户输入的字符为大写除了quit:

#!/bin/bash
#
read -p "Input something: " STRING

while [ $STRING != 'quit' ]; do
  echo $STRING | tr 'a-z' 'A-Z'
  read -p "Input something: " STRING
done

 

练习:每隔5秒查看hadoop用户是否登录,如果登录,显示其登录并退出;否则,显示当前时间,并说明hadoop尚未登录:

 

 

#!/bin/bash
#
who | grep "hadoop" &> /dev/null
RETVAL=$?

while [ $RETVAL -ne 0 ]; do
  echo "`date`, hadoop is not log." 
  sleep 5
  who | grep "hadoop" &> /dev/null
  RETVAL=$?
done

echo "hadoop is logged in."

 

 

写一个脚本:

1. 显示一个菜单给用户:

d|D) show disk usages.

m|M) show memory usages.

s|S) show swap usages.

*) quit.

 

2 当用户给定选项后显示相应的内容; 

扩展:

当用户选择完成,显示相应信息后,不退出;而让用户再一次选择,再次显示相应内容;除了用户使用quit

 

 

#!/bin/bash
#
cat << EOF
d|D) show disk usages.
m|M) show memory usages.
s|S) show swap usages.
*) quit.
EOF

read -p "Your choice: " CHOICE
while [ $CHOICE != 'quit' ];do
  case $CHOICE in
  d|D)
    echo "Disk usage: "
    df -Ph ;;
  m|M)
    echo "Memory usage: "
    free -m | grep "Mem" ;;
  s|S)
    echo "Swap usage: "
    free -m | grep "Swap" ;;
  *)
    echo "Unknown.." ;;
  esac

read -p "Again, your choice: " CHOICE
done    

 

 

2. 应用案例

㈠ 脚本计算1-50偶数和

 

#!/bin/env bash
sum=0
for ((i=0;i<=50;i+=2))
do
    let sum=$sum+$i  (let sum=sum+i)
done
echo "1-50的偶数和为:$sum"


#!/bin/bash
#定义变量
sum=0
i=2
#循环打印1-50的偶数和并且计算后重新赋值给sum
while [ $i -le 50 ]
do
    let sum=$sum+$i
    let i+=2  或者 $[$i+2]
done
#打印sum的值
echo "1-50的偶数和为:$sum"

 

 

㈡ 脚本同步系统时间

① 具体需求

 

    写一个脚本,30秒同步一次系统时间,时间同步服务器10.1.1.1
    如果同步失败,则进行邮件报警,每次失败都报警
    如果同步成功,也进行邮件通知,但是成功100次才通知一次

 

② 思路

 

- 每个30s同步一次时间,该脚本是一个死循环
  - while true;do  同步时间,然后休息30s(sleep 30)done
- 同步失败发送邮件
  - 在do.....done循环体之间加if...else...(判断同步失败还是成功)
- 同步成功100次发送邮件
  - 统计成功次数——>count=0——>成功1次加+1——>let count++

 

 

③ 落地实现

 

#!/bin/env bash
# 该脚本用于时间同步
NTP=10.1.1.1
count=0
while true
do
    ntpdate $NTP &>/dev/null
    if [ $? -ne 0 ];then
        echo "system date failed" |mail -s "check system date"  root@localhost
    else
        let count++
        if [ $count -eq 100 ];then
        echo "system date success" |mail -s "check system date"  root@localhost && count=0
        fi
    fi
sleep 30
done


#!/bin/bash
#定义变量
count=0
ntp_server=10.1.1.1
while true
do
    rdate -s $ntp-server &>/dev/null
    if [ $? -ne 0 ];then
        echo "system date failed" |mail -s 'check system date'  root@localhost    
    else
        let count++
        if [ $[$count%100] -eq 0 ];then
        echo "system date successfull" |mail -s 'check system date'  root@localhost && count=0
        fi
    fi
sleep 3
done

以上脚本还有更多的写法,课后自己完成

 

 

 

 

until循环结构

 

 与while的执行条件相反
当条件不成立时,循环执行语句序列

 特点:条件为假就进入循环;条件为真就退出循环

 

 

语法:

 

until 条件判断表达式
do 
    循环体
 done

 或者:

until expression   [ 1 -eq 1 ]  (( 1 >= 1 ))
    do
        command
        command
        ...
    done

 

 

 

 

 

 例子:

until [1 -eq 1 ]      #条件一直成立,会一直执行循环体,就是死循环
do  
   echo "test1"
   sleep   1
done

 

打印1-5数字

 

i=1
while [ $i -le 5 ]
do
    echo $i
    let i++
done

i=1
until [ $i -gt 5 ]
do
    echo $i
    let i++
done

 

 

auto_check_gw.sh 自动切换网关脚本

 

 

2. 应用案例

###㈠ 具体需求

    使用until语句批量创建10个用户,要求stu1—stu5用户的UID分别为1001—1005;
    stu6~stu10用户的家目录分别在/rhome/stu6—/rhome/stu10

 

 

㈡ 思路

 

    创建用户语句 useradd -u|useradd -d
    使用循环语句(until)批量创建用户 until循环语句结构
    判断用户前5个和后5个 条件判断语句

 

㈢ 落地实现

 

#!/bin/env bash
if [ -d /rhome ];then
    echo "/rhome目录已存在"
else
    mkdir /rhome
    echo "/rhome不存在,已完成创建"
fi

i=1
until [ $i -gt 10 ]
do
        if [ $i -le 5 ];then
                useradd -u $[1000+$i] stu$i
                echo 123|passwd --stdin stu$i

        else
                useradd -d /rhome/stu$i stu$i
                echo 123|passwd --stdin stu$i
        fi
let i++
done

==================================================

#!/bin/bash
i=1
until [ $i -gt 10 ]
do
    if [ $i -le 5 ];then
        useradd -u $[1000+$i] stu$i && echo 123|passwd --stdin stu$i
    else
        [ ! -d /rhome ] && mkdir /rhome
        useradd -d /rhome/stu$i stu$i && echo 123|passwd --stdin stu$i        
    fi
let i++
done

 

四、课后作业

 

    1.判断/tmp/run目录是否存在,如果不存在就建立,如果存在就删除目录里所有文件
    2.输入一个路径,判断路径是否存在,而且输出是文件还是目录,如果是链接文件,还得输出是 有效的连接还是无效的连接
    3.交互模式要求输入一个ip,然后脚本判断这个IP 对应的主机是否 能ping 通,输出结果类似于: Server 10.1.1.20 is Down! 最后要求把结果邮件到本地管理员root@localhost mail01@localhost
    4.写一个脚本/home/program,要求当给脚本输入参数hello时,脚本返回world,给脚本输入参数world时,脚本返回hello。而脚本没有参数或者参数错误时,屏幕上输出“usage:/home/program hello or world”
    5.写一个脚本自动搭建nfs服务

 

 

 嵌套循环

 

一个==循环体==内又包含另一个完整的循环结构,称为循环的嵌套。在外部循环的每次执行过程中都会触发内部循环,直至内部完成一次循环,才接着执行下一次的外部循环。for循环、while循环和until循环可以相互嵌套。

demo1:打印如下图案**

 

 

1
12
123
1234
12345

X轴:
for ((i=1;i<=5;i++));do echo -n $i;done
Y轴:
负责打印换行

#!/bin/bash
for ((y=1;y<=5;y++))
do
    for ((x=1;x<=$y;x++))
    do
        echo -n $x
    done
echo
done

#!/bin/bash
for ((y=1;y<=5;y++))
do
    x=1
    while [ $x -le $y ]
        do
        echo -n $x
        let x++
        done
echo
done

 

 

demo2:打印如下图案

 

5
54
543
5432
54321

Y轴:打印换行
X轴:打印数字 5-1

#!/bin/bash
y=5
while (( $y >= 1 ))
do
    for ((x=5;x>=$y;x--))
    do
        echo -n $x
    done
echo
let y--
done


#!/bin/bash
for (( y=5;y>=1;y--))
do
    for (( x=5;x>=$y;x--))
    do
    echo -n $x
    done
echo
done

#!/bin/bash
y=5
while [ $y -ge 1 ]
do
    for ((x=5;x>=$y;x--))
    do
    echo -n $x
    done
echo
let y--
done


#!/bin/bash
y=1
until (( $y >5 ))
do
    x=1
    while (( $x <= $y ))
    do
    echo -n $[6-$x]
    let x++
    done    
echo
let y++
done


课后打印:
54321
5432
543
54
5

 

课堂练习:打印九九乘法表(三种方法)

 

1
12
123
1234
12345

for ((y=1;y<=5;y++))
do
    for ((x=1;x<=$y;x++))
        do
        echo -n $x
        done
echo
done

1*1=1

1*2=2   2*2=4

1*3=3   2*3=6   3*3=9

1*4=4   2*4=8   3*4=12  4*4=16

1*5=5   2*5=10  3*5=15  4*5=20  5*5=25

1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36

1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49

1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64

1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81


Y轴:循环9次,打印9行空行
X轴:循环次数和Y轴相关;打印的是X和Y轴乘积 $[] $(())

#!/bin/bash
for ((y=1;y<=9;y++))
do
    for ((x=1;x<=$y;x++))
    do
        echo -ne "$x*$y=$[$x*$y]\t"
    done
echo
echo
done


#!/bin/bash
y=1
while [ $y -le 9 ]
do
        x=1
        while [ $x -le $y ]
        do
                echo -ne "$x*$y=$[$x*$y]\t"
                let x++
        done
echo
echo
let y++
done

或者
#!/bin/bash
for i in `seq 9`
do
    for j in `seq $i`
    do
        echo -ne  "$j*$i=$[$i*$j]\t"
    done
echo
echo
done
或者
#!/bin/bash
y=1
until [ $y -gt 9 ]
do
        x=1
        until [ $x -gt $y ]
        do
                echo -ne "$x*$y=$[ $x*$y ]\t"
                let x++
        done
echo
echo
let y++
done

 

 

 

 case分支语句

 

case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

 

 1. 语法结构

 

语法:

 

case 变量名 in 
    模式1)
           变量的值与模式1匹配时执行的操作;;
    模式2)
            变量的值与模式2匹配时执行的操作;;
     模式N)
             变量的值与模式N匹配时执行的操作;;
*)
    变量的值与以上所有模式都不匹配时执行的操作;;
esac

 

 或者:

case var in             定义变量;var代表是变量名
pattern 1)              模式1;用 | 分割多个模式,相当于or
    command1            需要执行的语句
    ;;                  两个分号代表命令结束
pattern 2)
    command2
    ;;
pattern 3)
    command3
    ;;
          *)              default,不满足以上模式,默认执行*)下面的语句
    command4
    ;;
esac                            esac表示case语句结束

 

 

case通常和函数一起使用,用来给服务写启动脚本。

 

 

 

 

 

只接受参数start,stop,restart,status其中之一

 

 

#!/bin/bash
#
DEBUG=0
ADD=0
DEL=0

for I in `seq 0 $#`; do
  if [ $# -gt 0 ]; then
      case $1 in
      -v|--verbose)
        DEBUG=1
        shift ;;
      -h|--help)
        echo "Usage: `basename $0` --add USER_LIST --del USER_LIST -v|--verbose -h|--help"
        exit 0
        ;;
      --add)
        ADD=1
        ADDUSERS=$2
        shift 2
        ;;
      --del)
        DEL=1
        DELUSERS=$2
        shift 2
        ;;
      *)
        echo "Usage: `basename $0` --add USER_LIST --del USER_LIST -v|--verbose -h|--help"
        exit 7
        ;;
    esac
  fi
done

if [ $ADD -eq 1 ]; then
  for USER in `echo $ADDUSERS | sed 's@,@ @g'`; do
    if id $USER &> /dev/null; then
      [ $DEBUG -eq 1 ] && echo "$USER exists."
    else
      useradd $USER
      [ $DEBUG -eq 1 ] && echo "Add user $USER finished."
    fi
  done
fi

if [ $DEL -eq 1 ]; then
  for USER in `echo $DELUSERS | sed 's@,@ @g'`; do
    if id $USER &> /dev/null; then
      userdel -r $USER
      [ $DEBUG -eq 1 ] && echo "Delete $USER finished."
    else
      [ $DEBUG -eq 1 ] && echo "$USER not exist."
    fi
  done
fi

 

 

一个脚本showlogged.sh其用法格式为

showlogged.sh -v -c -h|--help

 

其中,-h选项只能单独使用,用于显示帮助信息;-c选项时,显示当前系统上登录的所有用户数;如果同时使用了-v选项,则既显示同时登录的用户数,又显示登录的用户的相关信息;如

 

Logged users: 4. 

They are:
root     tty2         Feb 18 02:41
root     pts/1        Mar  8 08:36 (172.16.100.177)
root     pts/5        Mar  8 07:56 (172.16.100.177)
hadoop   pts/6        Mar  8 09:16 (172.16.100.177)

 

 

 

 

例子:

#!/bin/bash
#
declare -i SHOWNUM=0
declare -i SHOWUSERS=0

for I in `seq 1 $#`; do
  if [ $# -gt 0 ]; then
    case $1 in
    -h|--help)
      echo "Usage: `basename $0` -h|--help -c|--count -v|--verbose"
      exit 0 ;;
    -v|--verbose)
      let SHOWUSERS=1
      shift ;;
    -c|--count)
      let SHOWNUM=1
      shift ;;
    *)
      echo "Usage: `basename $0` -h|--help -c|--count -v|--verbose"
      exit 8 ;;
    esac
  fi
done

if [ $SHOWNUM -eq 1 ]; then
  echo "Logged users: `who | wc -l`."
  if [ $SHOWUSERS -eq 1 ]; then
    echo "They are:"
    who
  fi
fi

 

2. 应用案例

 

㈠ 脚本传不同值做不同事



**具体需求:**当给程序传入start、stop、restart三个不同参数时分别执行相应命令


#!/bin/env bash
case $1 in
        start|S)
        service apache start &>/dev/null && echo "apache 启动成功"
        ;;
        stop|T)
        service apache stop &>/dev/null && echo "apache 停止成功"
        ;;
        restart|R)
        service apache restart &>/dev/null && echo "apache 重启完毕"
        ;;
        *)
        echo "请输入要做的事情..."
        ;;
esac

 

 

㈡ 根据用户需求选择做事





**具体需求:**

脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭等操作


#!/bin/env bash
read -p "请输入你要管理的服务名称(vsftpd):" service
case $service in
        vsftpd|ftp)
        read -p "请选择你需要做的事情(restart|stop):" action
        case $action in
                stop|S)
                service vsftpd stop &>/dev/null && echo "该$serivce服务已经停止成功"
                ;;
                start)
                service vsftpd start &>/dev/null && echo "该$serivce服务已经成功启动"
                ;;
        esac
        ;;
        httpd|apache)
        echo "apache hello world"
        ;;
        *)
        echo "请输入你要管理的服务名称(vsftpd)"
        ;;
esac

 

㈢ 菜单提示让用户选择需要做的事

 

具体需求:

模拟一个多任务维护界面;当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作



**具体需求:**

模拟一个多任务维护界面;当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作

**********请选择*********
h    显示命令帮助
f    显示磁盘分区
d    显示磁盘挂载
m    查看内存使用
u    查看系统负载
q    退出程序
*************************

 


思路:


1. 菜单打印出来
2. 交互式让用户输入操作编号,然后做出相应处理

 



落地实现:

1. 菜单打印(分解动作)


#!/bin/env bash
cat <<-EOF
    h    显示命令帮助
    f    显示磁盘分区
    d    显示磁盘挂载
    m    查看内存使用
    u    查看系统负载
    q    退出程序
    EOF

 




2. 最终实现


#!/bin/bash
#打印菜单
cat <<-EOF
    h    显示命令帮助
    f    显示磁盘分区
    d    显示磁盘挂载
    m    查看内存使用
    u    查看系统负载
    q    退出程序
    EOF

#让用户输入需要的操作
while true
do
read -p "请输入需要操作的选项[f|d]:" var1
case $var1 in
    h)
    cat <<-EOF
        h       显示命令帮助
        f       显示磁盘分区
        d       显示磁盘挂载
        m       查看内存使用
        u       查看系统负载
        q       退出程序
    EOF
    ;;
    f)
    fdisk -l
    ;;
    d)
    df -h
    ;;
    m)
    free -m
    ;;
    u)
    uptime
    ;;
    q)
    exit
    ;;
esac
done

 

或者:

 

#!/bin/bash
#打印菜单
menu(){
cat <<-END
    h    显示命令帮助
    f    显示磁盘分区
    d    显示磁盘挂载
    m    查看内存使用
    u    查看系统负载
    q    退出程序
    END
}
menu
while true
do
read -p "请输入你的操作[h for help]:" var1
case $var1 in
    h)
    menu
    ;;
    f)
    read -p "请输入你要查看的设备名字[/dev/sdb]:" var2
    case $var2 in
        /dev/sda)
        fdisk -l /dev/sda
        ;;
        /dev/sdb)
        fdisk -l /dev/sdb
        ;;
    esac
    ;;
    d)
    lsblk
    ;;
    m)
    free -m
    ;;
    u)
    uptime
    ;;
    q)
    exit
    ;;
esac
done


课堂练习:

1. 输入一个等级(A-E),查看每个等级的成绩;如:输入A,则显示“90分~100分”,依次类推
2. 判断用户输入的字符串,如果是"hello",则显示"world";如果是"world",则显示"hello",否则提示"请输入hello或者world,谢谢!"

 

 

 

模拟2人第一次相亲的场景,使用read让用户输入它的名字,性别,年龄(年龄放在性别判断后);在case里面再嵌套case菜单,使之选项更丰富。

要求:1)

    对性别进行判断,如果不输入男或者女,则显示”你是泰国来的吗?“

    如果是男的,对其年龄进行判断。

 

2)

 

- 如果男的年龄大于等于18岁则显示“某某先生,你结婚了吗?”;
- 如果对方回答结了或者yes,则显示“结了你来这凑什么热闹”;
- 如果对方回答没有或者no,再次询问“那你有房有车吗?”;
- 如果既不说结了也不说没结则显示:“你到底结没结婚啊?”
- 如果回答有房有车,则显示”咱去民政局领证吧“;
- 如果回答没有,则显示“不好意思,我去下洗手间。”;
- 如果既不说有又不说没有,则显示“别浪费时间,请正面回答”。
- 如果男的年龄小于18岁,则显示“某某某你个小毛孩也来这凑热闹啦”

 

3)如果是女的,并且年龄大于等于18岁,则显示”某某女士你好“;否则显示”某某小姐你好“

参考:
#!/bin/bash
read -p "输入你的姓名:" name
read -p "输入你的性别:" gender

case "$gender" in|man|male|boy )
        read -p "输入你的年龄:" age
        if [ $age -ge 18 ];then
            read -p "$name先生,你结婚了吗?" anwser
                case "$anwser" in
                    结了|有|yes )
                        echo "结了你还来干嘛?"
                        ;;
                    没结|没有|没|no )
                        read -p "有房有车吗?" anwser2
                        case "$anwser2" in
                            有)
                                echo "咱就直接去民政局领证吧"
                                ;;
                            没有 )
                                echo "不好意思,我去下洗手间"
                                ;;
                            * )
                                echo "别浪费时间,请正面回答"
                        esac
                        ;;
                    * )
                        echo "你到底结没结?"
                esac

        else
            echo "$name小子"
        fi
        ;;
    女|woman|female|girl|lady )
        read -p "输入你的年龄:" age
        if [ $age -ge 18 ];then
            echo "$name女士"
        else
            echo "$name小姐"
        fi
        ;;
    * )
        echo "你是泰国来的吗?"
esac

该程序有个bug:如果输入年龄为负数或者0也是可以的,如何修复bug?增加一个条件:如果输入的年龄小于等于10则显示:”不跟你玩了。。。“

在最后加入如下语句即可:
if [ $age -ge 18 ];then
                        echo "$name女士"
                elif
                   [ $age -le 10 ];then
                        echo "不跟你玩了"
                     exit 1
                else
                        echo "$name小姐"
                fi
                ;;
        * )
                echo "你是泰国来的吗?"

esac

 

posted @ 2018-05-03 10:18  钟桂耀  阅读(307)  评论(0编辑  收藏  举报