美文网首页
shell编程进阶

shell编程进阶

作者: 毛利卷卷发 | 来源:发表于2018-06-24 10:01 被阅读0次

    流程控制

    if

    单条件

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

    双分支

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

    多分支

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

    case

    case支持glob风格的通配符:

    *: 任意长度任意字符
    ?: 任意单个字符
    []:指定范围内的任意单个字符
    a|b: a或b

    case 变量引用 in
        PAT1) 分支1;;
        PAT2) 分支2;;
        ...
        *) 默认分支;;
    esac
    

    编写一个脚本,实现执行它,则会显示当前的日期,格式为:2018年06月14日 15时30分18秒 星期四

    #!/bin/bash
    
    w=$(date +%w)
             case $w in
                    1) week="一";;
                    2) week="二";;
                    3) week="三";;
                    4) week="四";;
                    5) week="五";;
                    6) week="六";;
                    *) week="日";;
             esac
    echo "$(date "+%Y年%m月%d日 %H时%M分%S秒") 星期$week"
    

    for

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

    给系统中所有uid大于300的用户发一封邮件,内容:"你好,用户xxx, 端午快乐"

    #!/bin/bash
    for n in $(cat /etc/passwd |cut -d: -f3);do
        if [ $n -gt 300 ];then
        UserName=`id -un $n`
        echo "你好, 用户$UserName,端午快乐" |mail -s "端午祝福" $UserName
        fi
    done 
    

    while

    while CONDITION; do
        循环体
    done
    

    a=0,打印0-9

    #!/bin/bash
    a=0
    while [ $a -lt 10 ];do
        echo $a
        a=$[$a+1]
    done
    

    until

    与while相反,条件为加时循环

    until CONDITION; do
        循环体
    done
    

    a=0,打印0-9

    #!/bin/bash
    a=0
    until [ $a -gt 9 ];do
        echo $a
        a=$[$a+1]
    done
    

    continue

    提前结束第N层的本轮循环,最内层为第1层

    #!/bin/bash
    for n in {1..10};do
        if [ $n -eq 5 ];then
            continue
        fi
    echo $n
    done
    

    break

    提前结束第N层循环,最内层为第1层

    #!/bin/bash
    for n in {1..10};do
        if [ $n -eq 5 ];then
            break
        fi
    echo $n
    done
    

    shift

    用于将参量列表list左移指定次数,参量列表list一旦被移动,最左端的那个参数就从列表中删除

    #!/bin/bash
    while [ $# -gt 0 ];do 
        echo $*
        shift
    done
    

    编写一个依次创建指定用户的脚本,如/root/bin/createuser.sh zhangsan lisi wangsu

    #!/bin/bash
    while [ $# -gt 0 ];do
        if id $1 &> /dev/null ;then
            echo "$1 is already exist"
        else
            useradd $1
            echo "$1 created"
        fi
        shift
    done
    

    练习

    1. 编写脚本/root/bin/createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息

      #!/bin/bash
      read -p "Please enter a username: " username
      
      if `id ${username} &> /dev/null`;then
              echo "User already exists"
      else
              useradd ${username}
              echo "${username} is to created"
              cat /etc/passwd |grep "^${username}:"
      fi
      
    2. 编写脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的若是y,Y,Yes,YES,yES 等则显示YES,输入其他的显示NO

      #!/bin/bash
      read -p "Are you agree?yes or no: " bool
      
      bool=`echo ${bool}|tr [:upper:] [:lower:]`
      
      if [[ yes =~ ${bool} ]];then
              echo "Your answer is yes"
      else
              echo "your answer is no"
      fi
      
    3. 编写脚本/root/bin/filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,块设备,符号设备,管道,套接字)

      #!/bin/bash
      read -p "Please enter a path: " path
      
      if `ls ${path} > /dev/null`;then
              filetype=`ls -ld ${path} |grep -o "^."`
              case ${filetype} in
                      -) echo "The file is ordinary file";;
                      d) echo "The file is directory file";;
                      l) echo "The file is link file";;
                      b) echo "The file is block file";;
                      c) echo "The file is char file";;
                      p) echo "The file is pipe file";;
                      s) echo "The file is socket file";;
                      *) echo "The file is others";;
              esac
      else
              echo "No such file or directory"
      fi
      
    4. 编写脚本/root/bin/checkint.sh,判断用户输入的参数是否为正整数

      #!/bin/bash
      read -p "Please enter a string: " string
      
      if [[ ${string} =~ ^[[:digit:]]+$ ]];then
              echo "The string is int"
      else
              echo "The string is others"
      fi
      
    5. 编写脚本var_filetype.sh,判断/var/目录下所有文件的类型

      #!/bin/bash
      for filename in `ls /var`;do
      #       echo ${filename}
              filetype=`ls -ld "/var/${filename}" |grep -o "^."`
              case ${filetype} in
                      -) echo "${filename} is ordinary file";;
                      d) echo "${filename} is directory file";;
                      l) echo "${filename} is link file";;
                      b) echo "${filename} is block file";;
                      c) echo "${filename} is char file";;
                      p) echo "${filename} is pipe file";;
                      s) echo "${filename} is socket file";;
                      *) echo "${filename} is others";;
              esac
      done
      
      #!/bin/bash
      for filename in `ls /var`;do
              if [ -f /var/${filename} ];then
                      echo "${filename} is ordirary file"
              elif [ -d /var/${filename} ];then
                      echo "${filename} is directory file"
              elif [ -L /var/${filename} ];then
                      echo "${filename} is link file"
              elif [ -b /var/${filename} ];then
                      echo "${filename} is block file"
              elif [ -c /var/${filename} ];then
                      echo "${filename} is char file"
              elif [ -p /var/${filename} ];then
                      echo "${filename} is pipe file"
              elif [ -S /var/${filename} ];then
                      echo "${filename} is socket file"
              else
                      echo "${filename} is others"
          
              fi  
      done
      
      #!/bin/bash
      
      for file in $(find /var);do
          if [ -f $file ]; then
              echo "$file is a regular file"
          elif [ -b $file ]; then
              echo "$file is a block file"
          elif [ -c $file ]; then
              echo "$file is a character file"
          elif [ -d $file ]; then
              echo "$file is a directory"
          elif [ -h $file ]; then
              echo "$file is a symbolic link"
          elif [ -p $file ]; then
              echo "$file is a named pipe"
          elif [ -S $file ]; then
              echo "$file is a socket"
          fi
      done
      
    6. 添加10个用户user1-user10,密码为8位随机字符

      #!/bin/bash
      
      for n in $(seq 1 10);do
          useradd user$n &> /dev/null
          password=$(cat /dev/urandom |tr -cd [[:alpha:][:punct:]] |head -c8)
          echo ${password} | passwd --stdin user$n &> /dev/null
          echo "user$n add successed, password is ${password}"
      done
      
    7. 编写脚本rc3.d.sh,查看/etc/rc.d/rc3.d目录下分别有多个以K开头和以S开头的文件;分别读取每个文件,以K开头的输出为文件加stop,以S开头的输出为文件名加start,如K34filename stop S66filename start

      #!/bin/bash
      
      echo "Top K is `ls /etc/rc.d/rc3.d/|grep "^K"|wc -l`"
      echo "Top S is `ls /etc/rc.d/rc3.d/|grep "^S"|wc -l`"
      
      for filename in `ls /etc/rc.d/rc3.d/`;do
              top=`echo ${filename}|grep -o "^."`
              if [ ${top} == K ];then
                      echo "${filename} stop"
              elif [ ${top} == S ];then
                      echo "${filename} start"
              else
                      continue
              fi  
      done
      
      #!/bin/bash
      
      echo "Top K is `ls /etc/rc.d/rc3.d/|grep "^K"|wc -l`"
      echo "Top S is `ls /etc/rc.d/rc3.d/|grep "^S"|wc -l`"
      
      ls /etc/rc.d/rc3.d/|sed -n "s/^K.*/& stop/p"
      ls /etc/rc.d/rc3.d/|sed -n "s/^S.*/& start/p"
      
    8. 编写脚本sum.sh,提示输入正整数n的值,计算1+2+…+n的总和

      #!/bin/bash
      read -p "Please enter a int: " int
      
      sum=0
      
      for i in `seq 1 $int`;do
              let sum+=i
      done
      
      echo $sum
      
      #!/bin/bash
      read -p "Please enter a int: " int 
      
      seq -s + 1 $int|bc
      
    9. 编写脚本int3.sh,计算100以内所有能被3整除的整数之和

      #!/bin/bash
      sum=0
      
      for i in `seq 1 100`;do
              if [ `echo $[${i}%3]` == 0 ];then
                      sum=$[$sum+$i]
              else
                      continue
              fi  
      done
      
      echo $sum
      
    10. 编写脚本hoststate.sh,提示请输入网络地址,如192.168.0.1,判断输入的主机在线状态

      #!/bin/bash
      read -p "Please enter a IPv4 address: " ip
      
      if `ping -c1 -w1 ${ip} &> /dev/null`;then
              echo "The host is up"
      else
              echo "The host is down"
      fi
      
    11. 编写脚本99.sh,打印九九乘法表

      #!/bin/bash
      for i in `seq 1 9`;do
              for j in `seq 1 ${i}`;do
                      echo -e "$i*$j=$[$i*$j]\t\c"
              done
              echo
      done
      
    12. 编写脚本createhtml.sh,在/testdir目录下创建10个html文件,文件名格式为数字N(从1到10)加随机8个字母,如:1AbCdeFgH.html

      #!/bin/bash
      
      mkdir /testdir &> /dev/null
      
      for i in {1..10};do
         random_string=$(cat /dev/urandom |tr -cd [:alpha:] |head -c8)
         touch /testdir/$i${random_string}.html
      done
      
    13. 编写triangle.sh,脚本打印等腰三角形

      #!/bin/bash
      read -p "请输入需要生成的等腰三角形的行数:" zongline
      for curline in $(seq 1 $zongline);do
             spacenum=$[$zongline-$curline]
             anum=$[2*$curline-1]
             
             for kongge in $(seq 1 $spacenum);do
                     echo -e "" "\c" 
             done
             
             for a in $(seq 1  $anum);do
                     echo -e "a\c"
             done
             
             echo
      done 
      
    14. 编写脚本caipiao.sh,填5个数字,猜中多少按以下方式输出
      0 3 5 9 3
      如果猜中一个,则输出First blood
      如果猜中两个,则输出Double kill
      如果猜中三个,则输出Triple kill
      如果猜中四个,则输出Quadra kill
      如果全中,则输出“大吉大利,今晚吃鸡”

       #!/bin/bash
       read -p "Please enter a number: " a
       read -p "Please enter a number: " b
       read -p "Please enter a number: " c
       read -p "Please enter a number: " d
       read -p "Please enter a number: " e
       
       A=`echo $[$RANDOM%10]`
       B=`echo $[$RANDOM%10]`
       C=`echo $[$RANDOM%10]`
       D=`echo $[$RANDOM%10]`
       E=`echo $[$RANDOM%10]`
       
       echo "$A$B$C$D$E"
       
       a=`[ $a -eq $A ];echo $?`
       b=`[ $b -eq $B ];echo $?`
       c=`[ $c -eq $C ];echo $?`
       d=`[ $d -eq $D ];echo $?`
       e=`[ $e -eq $E ];echo $?`
       
       i=$[$a+$b+$c+$d+$e]
       
       case $i in
               5) echo "No kill";;
               4) echo "First blood";;
               3) echo "Double kill";;
               2) echo "Triple kill";;
               1) echo "Quadra kill";;
               0) echo "大吉大利,今晚吃鸡"
       esac
      
      #!/bin/bash
      
      lucky_num=$(cat /dev/urandom | head -100|tr -c [:digit:] "@" |tr -d @|cut -c1-5)
      
      echo "The lucky number has been generated"
      read -p "Please enter your results: " input_num
      
      echo "lucky_num is $lucky_num"
      echo "input_num is $input_num"
      
      get_num_lens=0
      for i in `seq 1 5`;do
         r1=`echo $lucky_num|grep -o "[[:digit:]]"| sed -n "$i p"`
         r2=`echo $input_num|grep -o "[[:digit:]]"| sed -n "$i p"`
         if [ $r1 -eq $r2 ];then
             get_num_lens=$[$get_num_lens+1]
         fi
      done
      
      case $get_num_lens in
         1) echo "First blood";;
         2) echo "Double kill";;
         3) echo "Triple kill";;
         4) echo "Quadra kill";;
         5) echo "大吉大利,今晚吃鸡";;
         *) echo "sorry~";;
      esac
      
    15. 编写脚本,求100以内所有正奇数之和

      #!/bin/bash
      sum=0
      for i in `seq 1 100`;do
              if [ $[$i%2] -eq 1 ];then
                      sum=$[$sum+$i]
              fi
      done
      echo sum=$sum
      
    16. 编写脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态,并统计在线和离线主机各多少

    17. 编写脚本,打印九九乘法表

    18. 编写脚本,利用变量RANDOM生成10个随机数字,输出这个10数字,并显示其中的最大值和最小值

      #!/bin/bash
      for i in `seq 1 10`;do
              j=$RANDOM
              echo $j
              if [ $i == 1 ];then
                      max=$j
                      min=$j
              else
                      if [ $j -ge $max ];then
                              max=$j
                      fi
                      if [ $j -le $min ];then
                              min=$j
                      fi
              fi
      done
      echo max:$max
      echo min:$min
      
    19. 编写脚本,实现打印国际象棋棋盘

      #!/bin/bash
      for i in {1..8};do
              for j in {1..8};do
                      if [ $[$i%2] == 1 ];then
                              if [ $[$j%2] == 1 ];then
                                      echo -e "\033[47;37m  \033[0m\c"
                              else
                                      echo -e "\033[40;37m  \033[0m\c"   
                              fi
                      else
                              if [ $[$j%2] == 1 ];then
                                      echo -e "\033[40;37m  \033[0m\c"   
                              else
                                      echo -e "\033[47;37m  \033[0m\c"
                              fi
                      fi
              done
              echo
      done
      
    20. 后续六个字符串:efbaf275cd. 4be9c40b8b. 44b2395c46. f8c8873ce0. b902c16c8b. ad865d2f63是通过对随机数变量RANDOM随机执行命令:
      echo $RANDOM|md5sum|cut –c1-10
      后的结果,请破解这些字符串对应的RANDOM值

      #!/bin/bash
      i=0
      while [ $i -le 32767 ];do
              str=`echo $i|md5sum|cut -c1-10`
      #       echo $str
              case $str in
                      efbaf275cd) echo "efbaf275cd:$i";;
                      4be9c40b8b) echo "4be9c40b8b:$i";;
                      44b2395c46) echo "44b2395c46:$i";;
                      f8c8873ce0) echo "f8c8873ce0:$i";;
                      b902c16c8b) echo "b902c16c8b:$i";;
                      ad865d2f63) echo "ad865d2f63:$i";;
              esac
              i=$[$i+1]
      done
      
    21. 随机生成10以内的数字,实现猜字游戏,提示比较大或小,相等则退出

      #!/bin/bash
      random=$[$RANDOM%10]
      while true;do
              read -p "Please enter a int less than 10: " i
              if [ $i -eq $random ];then
                      echo "bingo"
                      break
              elif [ $i -gt $random ];then
                      echo "greater"
              else
                      echo "less"
              fi
      done
      
    22. 用文件名做为参数,统计所有参数文件的总行数

      #!/bin/bash
      sum=0
      while [ $# -gt 0 ];do
          lines=`cat $1|wc -l`
          shift
          sum=$[$sum+$lines]
      done
      echo $sum
      
    23. 用二个以上的数字为参数,显示其中的最大值和最小值

    特殊用法

    while

    依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量xxx

    while read xxx; do
        循环体
    done < /PATH/FROM/SOMEFILE
    

    将COMMAND执行的结果依次读取然后将每行依次赋值给变量xxx

    COMMAND |while read xxx; do
        循环体
    done
    

    扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功

    #!/bin/bash
    while read line ;do
            gecos=$(echo $line |cut -d: -f5)
            if [ -z "$gecos" ];then
                    UserName=$(echo $line |cut -d: -f1)
                    usermod -c "$UserName 62985600" $UserName
                    echo "$UserName's gecos changed"
            fi
    done < /etc/passwd
    

    找出分区利用率大于10%的分区

    #!/bin/bash
    df |grep /dev/sd |while read line;do
            used=$(echo $line |tr -s " " % |cut -d% -f5)
            name=$(echo $line |cut -d" " -f1)
            if (( $used > 10 ));then
                    echo "$name will be full:$used%"
            fi
    done
    
    for ((i=1;i<=100;i++));do
            let sum+=i
    done
    echo sum=$sum
    

    for

    for ((控制变量初始化;条件判断表达式;控制变量的修正表达式));do
        循环体
    done
    

    select

    主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示PS3 提示符,等待用户输入。用户输入菜单列表中的某个数字,执行相应的命令。用户输入被保存在内置变量REPLY中。常于case配合

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

    设计点菜系统,列出主菜单,选择后选择后,提示用户,要选几份,输入完后提示是否还要继续选,如继续选,返回到主菜单,然后继续选,如不要继续选,则会打印消费单后退出

    #!/bin/bash
    PS3="Please choose your food: "
    echo > /tmp/list
    echo "请问吃什么"
    
    getBill() {
        for i in $(cat /tmp/list |cut -d: -f4|tr -d 元);do
            let total+=i 
        done
        echo -e "\n总计:${total}元" >> /tmp/list
        cat /tmp/list
    }
    
    getCount() {
        read -p "Please enter count(int): " count
        sum=$[$(echo $price|cut -d: -f2)*$count]
        echo "${price}元:${count}份:${sum}元" >> /tmp/list
        while true;do
            select ack in $*;do
                case $REPLY in
                    1)  break 2
                    ;;
                    2)  getBill
                        exit
                    ;;
                esac
            done
        done
    }
    
    getPrice() {
        while true;do
            select price in $*;do
                case $REPLY in
                    1)  echo $price;
                        getCount "继续点菜" "结算"
                        break 3
                    ;;
                    2)  echo $price;
                        getCount "继续点菜" "结算"
                        break 3
                    ;;
                    3)  echo $price;
                        getCount "继续点菜" "结算"
                        break 3
                    ;;
                    4)  break 3;;
                esac
            done
        done
    }
    
    while true;do
        select menu in 饭 面 饺子 不吃 结算;do
            case $REPLY in
                1)  getPrice "炒饭:10" "盖饭:12" "木桶饭:15" "返回"
                ;;
                2)  getPrice "炒面:15" "盖面:16" "拉面:10" "返回"
                ;;
                3)  getPrice "猪肉大葱:20" "素三鲜:15" "韭菜鸡蛋:18" "返回"
                ;;
                4)  exit
                ;;
                5)  getBill
                    exit
                ;;
            esac
        done
    done
    

    trap

    信号捕捉

    trap '触发指令' 信号:自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作

    trap '' 信号:忽略信号的操作

    trap '-' 信号:恢复原信号的操作

    trap -p:列出自定义信号操作

    #!/bin/bash
    trap 'echo "signal:SIGINT"' int
    trap -p
    
    for((i=0;i<=10;i++));do
        sleep 1
        echo $i
    done
    
    trap '' int
    trap -p
    
    for((i=11;i<=20;i++));do
        sleep 1
        echo $i
    done
    
    trap '-' int
    trap -p
    
    for((i=21;i<=30;i++));do
        sleep 1
        echo $i
    done
    

    函数

    语法一

    function f_name{
    ...函数体...
    }
    

    语法二

    function f_name(){
    ...函数体...
    }
    

    语法三

    f_name(){
    ...函数体...
    }
    

    返回值

    默认默认取决于函数中执行的最后一条命令的退出状态码

    也可以通过return自定义状态码,0表示无错误返回,1-255表示由错误返回

    定义

    交互式环境下定义
    将函数放在脚本文件中作为它的一部分
    可放在只包含函数的单独文件中

    载入函数文件

    ./source PATH/SOMEFILE:载入指定函数文件

    set function_name:检查函数是否已载入当前shell
    unset function_name:删除当前shell中的此函数

    分类

    内部函数:变量仅对当前函数有效,local定义局部变量
    全局函数:变量对当前shell有效,默认函数中的变量为全局变量
    环境函数:变量对当卡nshell及子shell有效,export -f/declare -xf定义全局变量

    参数

    在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

    #!/bin/bash
    func() {
        echo func:1st is $1
        echo func:2nd is $2
        echo func:all are $*
        echo func:the arg number is $#
        echo func:the func name is $0
        shift
        echo func:1st is $1
    }
    func a b c d 
    

    数组

    声明

    默认定义数组时,若不声明数组的类型,则设置为索引数组,若不指定索引,则用0,1,2,3,4...

    declare -a ARRAY_NAME:声明数值索引数组
    declare -A ARRAY_NAME: 声明关联索引数组,可以自定义索引格式

    注意:两者不可相互转化

    declare -a:显示所有数值索引数组
    declare -A:显示所有关联索引数组

    [root@centos6 ~]# declare -A height=([zhangsan]=180 [lisi]=170 [wangwu]=158)
    

    赋值

    ARRAY_NAME[INDEX]=VALUE

    ARRAY_NAME=(VAL1 VAL2 VAL3 ...)

    ARRAY_NAME=([0]=VAL1 [3]=VAL2 ...)

    read -a ARRAY

    引用

    ${ARRAY_NAME[INDEX]}:引用数组元素

    ${ARRAY_NAME[*]}:引用数组所有元素

    ${#ARRAY_NAME[*]}:数组的长度(数组中元素的个数)

    unset ARRAY[INDEX]:删除数组中的某元素,导致稀疏格式

    unset ARRAY:删除整个数组

    数据处理

    ${ARRAY[*]:offset:number}:数组切片,offset表示要跳过的元素个数,number表示要取出的元素个数

    ARRAY[${#ARRAY[*]}]=value:向数组中追加元素

    [root@centos6 ~]# declare -a|grep name
    declare -a name='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
    [root@centos6 ~]# echo ${name[*]:2:3}
    3 4 5
    

    示例

    写一脚本会生成一个名为digit的数组,包含10个随机数,再显示最大值,最小值

    #!/bin/bash
    getArr() {
        for((i=0;i<$1;i++));do
            random[$i]=$RANDOM
        done
        echo ${random[*]}
    }
    
    getMax() {
        while [ $# -gt 0 ];do
            if [ -z $max ];then
                max=$1
            else
                [ $1 -gt $max ] && max=$1
            fi
        shift
        done
        echo $max
    }
    
    getArr 10
    getMax ${random[*]}
    
    #!/bin/bash
    for i in {0..9}
    do
        array_random[$i]=$RANDOM
    done
    
    echo ${array_random[*]}
    
    max_num() {
        maxnum=${array_random[0]}
        for i in {1..9}
        do
            if [ ${array_random[$i]} -gt $maxnum ];then
                maxnum=${array_random[$i]}
            fi
        done
    
        echo '最大值:' $maxnum
    }
    
    min_num() {
        minnum=${array_random[0]}
    
        for i in {1..9}
        do
            if [ ${array_random[$i]} -lt $minnum ];then
                minnum=${array_random[$i]}
            fi
        done
    
        echo '最小值:' $minnum
    }
    
    max_num
    min_num
    

    定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件,并统计其下标为偶数的文件中的行数之和

    #!/bin/bash
    arrLog=(/var/log/*.log)
    
    for n in $(seq 0 $[${#arrLog[*]}-1]);do
        if [ $[$n%2] -eq 0 ];then
            echo ${arrLog[$n]}
            let lines+=$(cat ${arrLog[$n]}|wc -l) # let lines+=$(wc -l < ${array[$i]})
        fi
    done
    echo "lines: $lines"
    

    练习

    1. 编写函数,实现OS的版本判断

      #!/bin/bash
      judge_version() {
              version=$(cat /etc/redhat-release |sed -n "s/.* \([0-9]\)\..*/\1/p")
              [ $version == 6 ] && return 0 || return 1
      }
      judge_version
      
    2. 编写函数,实现取出当前系统eth0的IP地址

      #!/bin/bash
      get_ipv4() {
              ip=$(ifconfig $1| sed -n "s/[[:space:]]\+inet addr:\([0-9\|.]\+\) .*/\1/p")
              return 0
      }
      get_ipv4 eth0
      
    3. 编写函数,实现打印绿色OK和红色FAILED

      #!/bin/bash
      . /etc/init.d/functions
      
      print_green_ok() {
       action $1 true
      }
      
      print_red_failed() {
       action $1 false
      }
      
      print_green_ok nihao
      print_red_failed nihao
      
    4. 编写函数,实现判断是否无位置参数,如无参数,提示错误

      #!/bin/bash
      judge_arguments() {
       [ $1 == 0 ] && echo "Please enter arguments"
      }
      judge_arguments $#
      
    5. 编写服务脚本/root/bin/testsrv.sh,完成如下要求
      (1) 脚本可接受参数:start, stop, restart, status
      (2) 如果参数非此四者之一,提示使用格式后报错退出
      (3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功”
      考虑:如果事先已经启动过一次,该如何处理?
      (4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成”
      考虑:如果事先已然停止过了,该如何处理?
      (5) 如是restart,则先stop, 再start
      考虑:如果本来没有start,如何处理?
      (6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“SCRIPT_NAME is running...”
      如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...”
      其中:SCRIPT_NAME为当前脚本名

      #!/bin/bash
      srv_name=$(basename $0)
      
      srv_find() {
       find /var/lock/subsys/$srv_name &> /dev/null    
      }
      srv_echo() {
       echo "$srv_name is $1"
      }
      srv_start() {
       if srv_find;then
           srv_echo "running..."
       else
           touch /var/lock/subsys/$srv_name
           srv_echo "start"
       fi
      }
      
      srv_stop() {
       if srv_find;then
           rm -rf /var/lock/subsys/$srv_name 
           srv_echo "stop"
       else
           src_echo "stopped..."
       fi
      }
      
      srv_restart() {
       if srv_find;then
           rm -rf /var/lock/subsys/$srv_name 
           touch /var/lock/subsys/$srv_name 
       else
           touch /var/lock/subsys/$srv_name 
       fi
       srv_echo "restart"
      }
      
      srv_status() {
       srv_find && srv_echo "running..." || srv_echo "stopped..."
      }
      main() {
       case $1 in
           start)  srv_start
           ;;
           stop)   srv_stop
           ;;
           restart)srv_restart
           ;;  
           status) srv_status
           ;;
           *)  echo "Please enter a argument: start or stop or restart or status"
               exit
           ;;
       esac
      }
      
      main $1
      
    6. 编写脚本/root/bin/copycmd.sh
      (1) 提示用户输入一个可执行命令名称
      (2) 获取此命令所依赖到的所有库文件列表
      (3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下; 如:/bin/bash ==> /mnt/sysroot/bin/bash
      /usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
      (4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下: 如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
      (5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出

      #!/bin/bash
      
      get_path() {
       which $1|grep -o "/.*/$1$"
      }
      
      mkdir_all() {
       cd $2
       for i in $(dirname $1|tr / ' ');do
           mkdir $i &> /dev/null
           cd $i
       done
      }
      cp_cmd() {
       mkdir_all $(get_path $1) $2
       cp $(get_path $1) .
       for n in $(ldd $(get_path $1)|grep -o "/[^[:space:]]\+");do
           echo $n
           mkdir_all $n $2
           p $n .
       done
      }
      
      while true;do
       read -p "Please enter a executable cmd name or quit: " cmd
       [ $cmd == quit ] && exit
       if $(which $cmd &> /dev/null);then
           cp_cmd $cmd /app/nihao  
       else
           echo "Your answer is not a executable cmd"
       fi
      done
      
    7. 编写函数实现两个数字做为参数,返回最大值

      #!/bin/bash
      get_max() {
       [ $1 -gt $2 ] && max=$1 || max=$2
       [ $max -gt 255 ] && echo "Please enter a 0~255 int" || return $max
      }
      
      get_max $1 $2
      echo $?
      
    8. 斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2)
      利用函数,求n阶斐波那契数列

      
      
    1. 输入若干个数值存入数组中,采用冒泡算法进行升序或降序排序

      #!/bin/bash
      declare -A arr # 注意声明位置,在函数内声明就只作用在函数内
      get_arr() {
       for n in $(seq 1 $1);do
           index=$RANDOM
           arr[$index]=$index
       done
       echo ${arr[*]}
      }
      get_sort() {
       while [ ${#arr[*]} -gt 1 ];do
           max=0
           for i in ${arr[*]};do  # 注意删除元素后其他元素的索引不变
               [ $i -gt $max ] && max=$i
           done
           echo $max
           unset arr[$max]
       done
       echo ${arr[*]}
      }
      
      get_arr 10
      get_sort
      
    2. 将下图所示,实现转置矩阵matrix.sh,将列转换成行显示
      1 2 3 1 4 7
      4 5 6 ===> 2 5 8
      7 8 9 3 6 9

    #!/bin/bash
    get_matrix() {
     for i in $(seq 1 $1);do
         for j in $(seq 1 $2);do
             eval "arr$i[$j]=$[$RANDOM%10]"
         done
         eval echo "\${arr$i[*]}"
     done
    }
    
    trans_matrix() {
     for m in $(seq 1 $2);do
         for n in $(seq 1 $1);do
             eval echo -n "\${arr$n[$m]}' '"
         done
         echo
     done    
    }
    get_matrix 3 6
    echo
    trans_matrix 3 6   
    

    高级字符串

    切片

    ${#var}:返回字符串变量var的长度

    ${var:offset}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)

    ${var:offset:number}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分

    ${var: -length}:取字符串的最右侧几个字符,注意:冒号后必须有一空白字符

    ${var:offset:-length}:从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容

    ${var: -length:-offset}:先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容,注意:-length前空格

    取子串

    ${var#*word}:其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符之间的所有字符

    ${var##*word}:同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容

    ${var%word*}:其中word可以是指定的任意字符,自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符之间的所有字符

    ${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符

    查找替换

    ${var/pattern/substr}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之

    ${var//pattern/substr}: 查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之

    ${var/#pattern/substr}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之

    ${var/%pattern/substr}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之

    ${var/pattern}:删除var表示的字符串中第一次被pattern匹配到的字符串

    ${var//pattern}:删除var表示的字符串中所有被pattern匹配到的字符串

    ${var/#pattern}:删除var表示的字符串中所有以pattern为行首匹配到的字符串

    ${var/%pattern}:删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串

    转换大小写

    ${var^^}:把var中的所有小写字母转换为大写

    ${var,,}:把var中的所有大写字母转换为小写

    高级变量

    变量赋值.jpg

    declare

    声明指定类型的变量,常用选项:

    • -r:声明或显示只读变量
    • -i:声明或显示整数型变量
    • -a:声明或显示索引数组
    • -A:声明或显示关联数组
    • -f:显示已定义的所有函数名
    • -F:仅显示已定义的所有函数名
    • -x:声明或显示环境变量和函数
    • -l:声明变量为小写字母
    • -u:声明变量为大写字母

    eval

    eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量.该命令对变量进行两次扫描

    eval tempvar=$$variable1
    tempvar=${!variable1}

    [root@server ~]# CMD=whoami
    [root@server ~]# echo $CMD
    whoami
    [root@server ~]# eval $CMD
    root
    [root@server ~]# n=10
    [root@server ~]# echo {0..$n}
    {0..10}
    [root@server ~]# eval echo {0..$n}
    0 1 2 3 4 5 6 7 8 9 10
    
    [root@server ~]# N=NAME
    [root@server ~]# NAME=fanjie
    [root@server ~]# N1=${!N}
    [root@server ~]# echo $N1
    wangxiaochun
    [root@server ~]# eval N2=\$$N
    [root@server ~]# echo $N2
    fanjie
    

    mktemp

    创建并显示临时文件,可避免冲突,常用选项:

    • -d:创建并显示临时目录
    [root@centos6 ~]# mktemp /app/tempXX
    mktemp: too few X's in template `/app/tempXX'
    [root@centos6 ~]# mktemp /app/tempXXX
    /app/tempmEL
    [root@centos6 ~]# mktemp
    /tmp/tmp.7ey38eMKh8
    
    [root@centos6 ~]# mktemp -d /app/tempXXXXXXXX
    /app/tempYb2pAQ7c
    [root@centos6 ~]# mktemp -d
    /tmp/tmp.HHoPgpEe4K
    

    install

    安装复制文件,可指定权限属主和属组,install [OPTION]... SOURCE... DIRECTORY,常用选项:

    • -T SOURCE DEST:单文件
    • -t DIRECTORY SOURCE
    • -d DIRECTORY...:创建空目录
    • -m MODE:指定权限,默认是755
    • -o OWNER:指定属主
    • -g GROUP:指定数组
    [root@centos6 ~]# install -m 700 -o fanjie -g admins /etc/passwd /app/
    [root@centos6 ~]# install –m 770 –d /testdir/installdir
    

    expect

    expect 是由Don Libes基于Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助Expect处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率,常用选项:

    • -c:从命令行执行expect脚本,默认expect是交互地执行的
    • -d:可以输出输出调试信息

    expect中相关命令

    spawn:启动新的进程
    send:用于向进程发送字符串
    expect:从进程接收字符串
    interact:允许用户交互
    exp_continue:匹配多个字符串在执行动作后加此命令

    #!/usr/bin/expect
    spawn scp /etc/fstab 192.168.8.100:/app
    expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send “magedu\n" }
    }
    expect eof
    
    #!/usr/bin/expect
    spawn ssh 192.168.8.100
    expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send “magedu\n" }
    }
    interact
    #expect eof
    
    #!/usr/bin/expect
    set ip 192.168.8.100
    set user root
    set password magedu
    set timeout 10
    spawn ssh $user@$ip
    expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "$password\n" }
    }
    interact
    
    #!/usr/bin/expect
    set ip [lindex $argv 0]
    set user [lindex $argv 1]
    set password [lindex $argv 2]
    spawn ssh $user@$ip
    expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "$password\n" }
    }
    interact
    #./ssh3.exp 192.168.8.100 root magedu
    
    #!/usr/bin/expect
    set ip [lindex $argv 0]
    set user [lindex $argv 1]
    set password [lindex $argv 2]
    set timeout 10
    spawn ssh $user@$ip
    expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "$password\n" }
    }
    expect "]#" { send "useradd haha\n" }
    expect "]#" { send "echo magedu |passwd --stdin haha\n" }
    send "exit\n"
    expect eof
    #./ssh4.exp 192.168.8.100 root magedu
    
    #!/bin/bash
    ip=$1
    user=$2
    password=$3
    expect <<EOF
    set timeout 10
    spawn ssh $user@$ip
    expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "$password\n" }
    }
    expect "]#" { send "useradd hehe\n" }
    expect "]#" { send "echo magedu |passwd --stdin hehe\n" }
    expect "]#" { send "exit\n" }
    expect eof
    EOF
    #./ssh5.sh 192.168.8.100 root magedu
    

    相关文章

      网友评论

          本文标题:shell编程进阶

          本文链接:https://www.haomeiwen.com/subject/bpejyftx.html