zoukankan      html  css  js  c++  java
  • 循环语句和函数

    4章 循环语句和函数
    编写一个好脚本的要素
    逻辑结构定义清晰
    脚本可以重用
    多加入注释
    学会调试脚本
    echo配合exit命令或sleep命令
    bash -x 脚本
    知识要点
    while语句、shift命令
    case语句
    shell函数应用
    while语句的结构
    重复测试某个条件,只要条件成立则反复执行
    while语句的各种用法
    注意:while通过管道,会产生一个新的bash(shell)
    While语句应用示例
    每5分钟输出一次时间到/tmp/time.txt
    禁止使用计划任务
    #!/bin/bash
    while true //死循环
    do
    date +%H%M >> /tmp/time.txt
    sleep 300
    done
    批量添加用户
    用户名称以stu开头,按数字顺序进行编号
    一共添加20个用户,即stu1、stu2、……、stu20
    初始密码均设为123456
    [root@localhost ~]# cat uaddwhile.sh
    #!/bin/bash
    PREFIX="stu"
    i=1
    while (( $i <= 20 )) //循环条件:序号<=20
    do
        useradd ${PREFIX}$i
        echo "123456" | passwd --stdin ${PREFIX}$i &> /dev/null
     (( i++ ))
    done
    [root@localhost ~]# ./uaddwhile.sh
    [root@localhost ~]# grep "stu" /etc/passwd | tail -3
    stu18:x:1028:1028::/home/stu18:/bin/bash
    stu19:x:1029:1029::/home/stu19:/bin/bash
    stu20:x:1030:1030::/home/stu20:/bin/bash
    使用inotify-tools,实现自动同步备份
    [root@localhost ~]# cat /opt/rsync.sh 
    inotifywait -mrq -e modify,create,move,delete /bak/ | while read DIR EVENT FILE
    do
            rsync -az --delete /bak/ rsync://10.10.10.1/share &>/dev/null
    done
    [root@localhost ~]# inotifywait -mrq -e modify,create,move,delete /bak/
    /bak/ CREATE passwd
    /bak/ MODIFY passwd
    /bak/ DELETE passwd
    分析当前主机中所有用户,哪些是普通用户,哪些是系统用户
    #!/bin/bash
    cat /etc/passwd | cut -d: -f1,3 | tr : ' ' > file1
    while read user uid
    do
     if (( uid>=500 && uid<=600 ))
      then
         echo "$user是普通用户"
     else
         echo "$user是系统用户"
      fi
    done < file1

    猜商品价格游戏
    通过变量RANDOM获得随机数
    提示用户猜测并记录次数,猜中后退出循环
    #!/bin/bash
    PRICE=$(expr $RANDOM % 1000)
    TIMES=0
    echo "商品实际价格为0-1000之间,猜猜看是多少?"
    while true #循环条件:ture
    do
     read -p "请输入你猜测的价格数目:" INT
     let TIMES++
     if [ $INT -eq $PRICE ] ; then #提示猜测并记录次数
     echo "恭喜你答对了,实际价格是 $PRICE"
     echo "你总共猜测了 $TIMES 次"
     exit 0                 #若猜中则退出脚本
     elif [ $INT -gt $PRICE ] ; then #与实际价格比较,给出提示
     echo "太高了!"
     else
     echo "太低了!"
     fi
    done
    [root@localhost ~]# ./pricegame.sh
    商品实际价格为0-999之间,猜猜看是多少?
    请输入你猜测的价格数目:500
    太高了!
    请输入你猜测的价格数目:250
    太低了!
    请输入你猜测的价格数目:375
    太高了!
    请输入你猜测的价格数目:280
    太高了!
    请输入你猜测的价格数目:265
    太高了!
    请输入你猜测的价格数目:253
    恭喜你答对了,实际价格是 253
    你总共猜测了 6
    shift迁移语句
    用于迁移位置变量,将 $1~$9 依次向左传递
    例如,若当前脚本程序获得的位置变量如下:
    $1=file1、$2=file2、$3=file3、$4=file4
    则执行一次shift命令后,各位置变量为:
    $1=file2、$2=file3、$3=file4
    再次执行shift命令后,各位置变量为:
    $1=file3、$2=file4
    应用示例:
    通过命令行参数传递多个整数值,并计算总和
    [root@localhost ~]# vi test.sh
    #!/bin/bash
    n=1
    while (($#  > 0))
    do
      echo $$n is $1 && ((n++))
      shift
    done
    应用示例:
    通过命令行参数传递多个整数值,并计算总和
    [root@localhost ~]# vi showday.sh
    #!/bin/bash
    Result=0
    while (($#  > 0))
    do
     Result=$((Result+$1))
        shift
    done
    echo "The sum is : $Result“
    [root@localhost ~]# ./sumer.sh  12 34 56
    The sum is : 102 
    case语句的结构
    针对变量的不同取值,分别执行不同的命令序列
    case语句应用示例
    击键类型识别
    提示用户输入一个字符
    判断出该字符是字母、数字或者其他字符
    #!/bin/bash
    read -p "请输入一个字符,并按Enter键确认:" KEY
    case "$KEY" in
     [a-z]|[A-Z])
     echo "您输入的是 字母。"
     ;;
     [0-9])
     echo "您输入的是 数字。"
     ;;
      *)
     echo "您输入的是 空格、功能键或其他控制字符。"
    esac
    [root@localhost ~]# ./hitkey.sh
    请输入一个字符,并按Enter键确认:k
    您输入的是 字母 k 。
    [root@localhost ~]# ./hitkey.sh
    请输入一个字符,并按Enter键确认:8
    您输入的是 数字 8 。
    [root@localhost ~]# ./hitkey.sh
    请输入一个字符,并按Enter键确认:^[[19~
    您输入的是 空格、功能键或其他控制字符。
    数字范围识别
    判断分数范围,分出优秀、合格、不合格三档
    [root@localhost ~]# cat test.sh
    read -p "请输入您的分数(0-100):" GRADE
    case "$GRADE" in
      100|9[0-9]|8[5-9])
     echo "$GRADE 分!优秀"
     ;;
      8[0-4]|[67][0-9])
     echo "$GRADE 分,合格" #大于等于85小于等于100
     ;;
      *)
     echo "$GRADE 分?不合格" #大于等于60小于等于84
     ;;
    esac
    编写系统服务脚本
    使用start、stop、restart等参数来控制服务
    服务控制指令通过位置变量$1传入
    能够通过chkconfig命令来管理此服务
    [root@localhost ~]# cat /etc/init.d/myprog
    #!/bin/bash
    # chkconfig: - 90 10      #用于chkconfig识别的配置
    # description: Startup script for sleep Server
    case "$1" in #根据$1传入的控制指令分别执行不同操作
    start)
     echo 正在启动$0服务
     ;;
    stop)
     echo 正在停止$0服务
     ;;
    restart|reload)
     $0 stop
     $0 start
     ;;
    *)
     echo "用法: $0 {start|stop|restart}"
    esac
    Shell函数应用
    Shell函数概述
    • 在编写Shell脚本程序时,将一些需要重复使用的命令操作,定义为公共使用的语句块,即可称为函数
    • 合理使用Shell函数,可以使脚本内容更加简洁,增强程序的易读性,提高执行效率
      • 简化代码
      • 维护成本低
      • 可读性好
      • 功能单一
    • 功能模块化,方便协同合作
    Shell函数由两部分组成:
    函数名(在一个脚本中必须唯一)
    函数体(命令或语句集合)
    定义新的函数(必须先定义后使用)
    调用已定义的函数
    向函数内传递参数(可以使用位置参数)
    Shell函数应用示例
    应用示例:
    在脚本中定义一个加法函数,用于计算2个整数的和
    调用该函数计算(12+34)、(56+789)的和
    #!/bin/bash
    adder() {
     echo $(($1 + $2))
    }
    adder 12 34
    adder 56 789
    [root@localhost ~]# ./adderfun.sh
    46
    845 
    新安装的mysql配置管理员密码的函数
    默认新安装的mysql管理员密码为空
    mysqlpass()
    {
         mysql -u root -e "use test;"
     if (($?==0))
     then
           mysqladmin -uroot password '123' && echo "mysql密码修改成功"
     else 
     echo "mysql无法修改密码" 
     exit 1
    fi
    }
    检查rpm包的函数
    如果rpm已经安装,则显示“包 is installed”
    如果rpm包未安装,则用yum安装,安装好显示“包 install ok
    checkRPM() {
     for i   #就是for i in $@获取所有位置参数
     do
     if ! rpm -q "$i" &> /dev/null
     then
     echo "please wait a moment"
                            yum install "$i" -y &> /dev/null
     echo "$i install ok"
     else
     echo "$i is installed"
     fi
     done
    }
    checkRPM httpd mysql mysql-server php php-mysql
    [root@localhost ~]# ./test.sh
    httpd is installed
    please wait a moment
    mysql install ok
    please wait a moment
    mysql-server install ok
    please wait a moment
    php install ok
    please wait a moment
    php-mysql install ok
    检查rpm包的函数
    如果rpm已经安装,则显示“包 is installed”
    如果rpm包未安装,则用yum安装,安装好显示“包 install ok”
    如果希望是在运行脚本的时候输入位置参数
    若要./test1.sh httpd mysql-server php php-mysql
    checkRPM() {
     for i   #就是for i in $@获取所有位置参数
     do
     if ! rpm -q "$i" &> /dev/null
     then
     echo "please wait a moment"
                            yum install "$i" -y &> /dev/null
     echo "$i install ok"
     else
     echo "$i is installed"
     fi
     done
    }
    checkRPM $@

    [root@localhost ~]# ./22.sh httpd mysql mysql-server php php-mysql
    httpd is installed
    please wait a moment
    mysql install ok
    please wait a moment
    mysql-server install ok
    please wait a moment
    php install ok
    please wait a moment
    php-mysql install ok
    函数返回值
    函数的退出状态有两种方式
    默认退出状态:函数的最后一条命令返回的退出状态
    使用return命令:以特定的退出状态退出函数
    return只能在函数内部使用,默认是0
    函数内部return之后的代码不会内执行
    使用函数输出:直接将函数的结果赋值给变量
    函数默认退出状态:函数的最后一条命令返回的退出状态
    [root@localhost ~]# cat test1.sh
    func() {
       rpm -q sadfsdf &> /dev/null 
     echo '$?' is $? 
     ls /etc/passwd
    }
    func
    echo func exit status is $?
    [root@localhost ~]# bash test2.sh
    $? is 1
    /etc/passwd
    func exit status is 0
    [root@localhost ~]# cat test2.sh
    func() {
     ls /etc/passwd
     echo '$?' is $? 
       rpm -q sadfsdf &> /dev/null 
    }
    func
    echo func exit status is $?
    [root@localhost ~]# bash test2.sh
    /etc/passwd
    $? is 0
    func exit status is 1

    函数使用retun命令指定退出状态,只能用于函数返回值。
    addUser()
    {
     for i in $@
     do
     if [[ $i == haha ]]; then
     return 14     #return指定退出状态,后面的语句不会执行
     else
     useradd $i
     fi
     done
    }
    addUser $@
    echo $?
    [root@localhost ~]# ./test.sh xixi haha  hehe
    14
    [root@localhost ~]# grep hehe /etc/passwd
    [root@localhost ~]#    #hehe没有创建
    直接将函数的结果赋值给变量
    [root@localhost ~]# cat test.sh
    func ()
    {
     du -sh $1 | tr -d '	'| cut -d'/' -f1 
    }
    size=$(func $1) #直接将函数的输出赋值给变量
    echo $1目录占用空间是:$size
    [root@localhost ~]# ./test.sh /etc
    /etc目录占用空间是:33M
    [root@localhost ~]# ./test.sh /usr/local
    /usr/local目录占用空间是:140K
    直接将函数的结果赋值给变量
    函数比内部命令优先
    num1(){
    for i in $(seq $1) ; do
     echo -n 1
    done
    }
    num2(){
    ((j=32-$1))
    for i in $(seq $j) ; do
     echo -n 0
    done
    }
    a=$(num1 $1)
    b=$(num2 $1)
    echo $a$b
    函数中的变量
    函数使用两种变量
    • 全局变量:在函数内部定义的变量,当前脚本主代码可以获取,脚本主代码定义变量,函数内部也可以获取
    • 局部变量:local 变量名,确保变量仅在函数内部使用 ,只能在函数内部使用
    func ()
    {
     echo a is $a
      b=100
    }
    a=200
    func
    echo b is $b
    func ()
    {
     echo a is $a
      local b=100
    }
    a=200
    func
    echo b is $b
    函数库文件应用
    函数库文件
    如果多个脚本需要调用重复的函数,没必要在每个脚本中定义,只需要创建函数库文件,将需要的函数都放到这个库文件
    每个脚本只需要一条语句调用库文件即可
    注意不能把库文件当做普通脚本一样在脚本中运行,那样那些函数将不会出现在脚本中
    可以存放公共
    创建库文件
    在脚本中调用库文件(注意库文件的路径)
    还可以将库文件在.bashrc中定义
    source /opt/lib.sh
    全局函数可以在当前shell的所有子shell中随意运行
    exprot -f port     #将函数输出为全局函数
    案例
    实验案例1
    编写脚本生成2位的随机数,要求个位和十位数不能相同,如果遇到个位和十位相同的就退出脚本(注意十位数不能为0)
    [root@localhost ~]# exp1
    61
    57
    81
    51
    51
    65
    31
    连续成功7次
    [root@localhost ~]# exp1
    连续成功0次
    [root@localhost test4]# exp1
    23
    连续成功1次
    [root@localhost test4]# exp1
    85
    46
    74
    97
    连续成功4次
    实验案例2
    给登录到当前主机的所有非root用户终端发送一句话“用户名: Hi,I’m Root!”
    [root@localhost ~]# vi hello.sh
    #!/bin/bash
    who | grep -v ^root | tr -s ' ' | cut -d ' ' -f1,2 > file1
    while read user tty
    do
     echo "$user : Hi , I'm Root!" > /dev/$tty
    done < file1
    实验案例3
    根据前面猜价格的脚本,编写猜10以内的随机数的脚本,显示效果如下
    每个人只有3次机会,超过3次没有猜中自动退出
    实验案例4
    编写del.sh脚本实现批量删除用户,脚本要求如下(使用while循环读取来改写上次的for脚本)
    提示输入需要删除的用户名前缀,如果用户名前缀为空或者空格,就显示“请输入合法用户名前缀”,然后退出脚本,每删除一个用户,要显示“用户用户名 已经成功被删除”。如果没有可以删除的用户,就显示“以用户前缀开头的用户不存在”。最后要显示删除的用户总数是“一共新建的用户数:数目”
    注意不能删除管理员或者系统用户(UID小500或者大于60000)
    实验案例6
    编写脚本显示如下图所示效果,要求选择一个菜单后,不用按回车,马上实现菜单相应的功能,显示完毕按任意键回到菜单,输入0退出菜单
                            ***Menu***
            1.      Display disk space
            2.      Display interface information
            3.      Display memory usage
            0.      Exit menu
                    Enter option: 4
    Sorry,wrong selection
                            Press any key to continue
                            ***Menu***
            1.      Display disk space
            2.      Display interface information
            3.      Display memory usage
            0.      Exit menu
                    Enter option:
    文件系统              容量  已用  可用 已用%% 挂载点
    /dev/sda2             367G  9.6G  339G   3% /
    tmpfs                 1.9G  124K  1.9G   1% /dev/shm
    /dev/sda1              97M   30M   63M  33% /boot
                            Press any key to continue
    实验案例7
    检查服务的函数,显示效果如图所示
    检查服务状态,启动的要显示正常,未启动的要重启
    重启后再次检查,还未启动的,要显示服务有问题
    要检查服务名称是否错误
    先停止FTP服务,然后故意改错FTP的配置文件
    [root@localhost ~]# service vsftpd stop       
    关闭 vsftpd:                                              [确定]
    [root@localhost ~]# echo dsalkjf >> /etc/vsftpd/vsftpd.conf
    [root@localhost ~]# ./test.sh smb vsftpd httpd              
    smb 状态正常
    500 OOPS: missing value in config file for: dsalkjf
    vsftpd服务有问题
    httpd 状态正常
    [root@servera test5]# ./test.sh  httpd samba  服务名称错误
    httpd重启后已经ok
    samba服务不存在,请检查问题
    实验案例8
    检查侦听端口的函数,显示效果如图所示
    输入一个参数检查侦听的端口,如果有侦听,显示端口和侦听的进程名称(要区分是udp还是tcp),如果端口没有侦听,则显示“端口未侦听”
    [root@localhost ~]# ./test.sh 21
    vsftpd正在侦听:tcp21端口
    [root@localhost ~]# ./test.sh 980
    rpcbind正在侦听:udp980端口
    [root@localhost ~]# ./test.sh 53
    53端口没有侦听
    实验案例9
    编写时间检查脚本
    编写时间检查函数,算出当前时间离各个时间参照点的小时分钟差值,显示效果如下
    2013年 07月 18日 星期四 08:40:00 CST
    [root@servera opt]# ./time.sh
    离上午上课还差0小时5分钟
    [root@servera opt]# date -s 845
    2013年 07月 18日 星期四 08:45:00 CST
    [root@servera opt]# ./time.sh 
    上午上课时间到,躁起来,娭毑们
    [root@servera opt]# date -s 1121
    2013年 07月 18日 星期四 11:21:00 CST
    [root@servera opt]# ./time.sh  
    离中午吃饭时间还有0小时24分钟
    [root@servera opt]# date -s 1145
    2013年 07月 18日 星期四 11:45:00 CST
    [root@servera opt]# ./time.sh  
    开饭喽,同志们冲啊
    [root@servera opt]# date -s 13:01
    2013年 07月 18日 星期四 13:01:00 CST
    [root@servera opt]# ./time.sh   
    午休,离下午上课时间还有0小时59分钟
    [root@servera opt]# date -s 1539
    2013年 07月 18日 星期四 15:39:00 CST
    [root@servera opt]# ./time.sh  
    离放学时间还有1小时51分钟
    实验案例10
    编写获取配置文件中的数据的脚本
    有多个脚本需要从配置文件中获取数据:
    IP、端口、服务名、用户名、密码
    写公共函数,通过参数来实现获取上面需要的dsN的数据
    编写2个脚本,导入公共函数,然后调用 函数,获取数据
  • 相关阅读:
    8086汇编学习小记王爽汇编语言实验12
    8086汇编学习小记王爽汇编语言课程设计1
    activeMQ 持久化配置 kevin
    snmpwalk kevin
    tcp benchmark kevin
    apache camel 条件路由 kevin
    netty 并发访问测试配置 kevin
    snmp常见操作 kevin
    转发:RocketMQ与kafka的对比 kevin
    centos jdk 下载 kevin
  • 原文地址:https://www.cnblogs.com/qluzzh/p/10322536.html
Copyright © 2011-2022 走看看