bash中for循环语句:适用于循环变量已知的场景
for 循环语句格式:
 for VAR_NAME in LIST;do
  循环体
 done
其中list列表中包含一个或多个元素,列表的生成方式:
 1、手动给定列表,如:for i in 1 2 3 4 5;do列表元素之间用空格隔开
 2、数值列表:{1..100}、`seq 1 100`;
 3、$*,$@,使用用户提供的参数列表作为循环列表
 4、使用命令生成列表。
注:for循环的list是以空格分开的多个元素

示例:
 求100以内所有整数的和(循环列表是数值列表的方式)
 #!/bin/bash
 #
 sum=0
 for i in {1..100};do
  sum=$[$sum+$i]
 done
 echo $sum
 求100以内奇数的和(循环列表由数值列表生成)
 #!/bin/bash
 #
 sum=0
 for i in `seq{1,2,100}`;do
   sum=$[$sum+$i]
 done
 echo $sum
 求当前系统上所有用户的ID之和(循环列表由命令生成)
 #!/bin/bash
 #
 sum=0
 for i in `cut -d: -f3 /etc/passwd`;do
  sum=$[$sum+$i]
 done
 echo "user id sum is $sum"
 分别统计/etc/rc.d/rc.sysinit、/etc/rc.d/init.d/functions和/etc/inittab文件中以#开头的行的行数和空白行数;(循环列表手动指定)
 #!/bin/bash
 #

 for i in /etc/rc.d/rc.sysinit /etc/rc.d/init.d/functions /etc/inittab ;do
  line1=`grep "^#" $i | wc -l | cut -d' ' -f1`
  line2=`grep "^$" $i | wc -l | cut -d' ' -f1`
  echo "$i has # lines $line1.blank lines $line2."
 done

bash算数运算
 在bash中变量属于弱类型变量,可以随时声明使用,默认情况下数值类型的变量也会被理解为字符保存,如果没有提前定义整型变量,字符型的数字依然可以参与算术运算,bash会执行变量类型的隐式转换,若定义整型变量,可以使用let和declare声明变量:
  let VAR_NAME=INTEGER_VALUE,如let a=3
  declare -i VAR_NAME=INTEGER_VALUE,如declare -i a=3
 bash中实现算术运算的方式如下:
  let VAR_NAME=ARITHMATIC_EXPRESSION
  VAR_NAME=$[ARITHMATIC_EXPRESSION]
  VAR_NAME=$((EXPRESSION))
  VAR_NAME=$(expr $num1 + $num2)
 bash中常用的算术运算符有:+、-、*、/、%、**
 示例:
 求100以内所有整数的和(循环列表是数值列表的方式)
 #!/bin/bash
 #
 sum=0
 for i in {1..100};do
  sum=$[$sum+$i]
 done
 echo $sum
bash中的逻辑运算
 常见的逻辑运算有:与、或、非、异或
 逻辑运算的运算法则:
 与运算:
  真,假:
    真 && 真 = 真
    真 && 假 = 假
    假 && 真 = 假
    假 && 假 = 假

  或运算:
   真,假
    真 || 真 = 真
    真 || 假 = 真
    假 || 真 = 真
    假 || 假 = 假

  非运算:
   非运算即是对操作数进行取反操作
    !真 = 假
    !假 = 真

  异或运算:
    真 XOR 真 = 假
    假 XOR 假 = 假
    真 XOR 假 = 真
    假 XOR 真 = 真

bash中的条件测试
 执行条件测试的语法格式:
  test EXPR
  [ EXPR ]
  ` EXPR `
 根据比较时的操作数的类型,将测试类型分为以下几种:
  整型测试:整数比较
  字符测试:字符串比较
  文件测试:判断文件的存在性及属性等
 注:比较运算通常只在同一类型的数值之间进行比较
 整型测试的操作符:-gt(大于)、-lt(小于)、-ge(大于等于)、-le(小于等于)、-eq(等于)、-ne(不等于)
 字符串测试的操作符分为两种:
  双目操作符:>(大于)、<(小于)、==(等于)、!=(不等于)、=~(字符串匹配)
  增强型赋值操作符:+=, -=, *=, /=, %=
  单目:-n String: 是否不空,不空则为真,空则为假
     -z String: 是否为空,空则为真,不空则假

 示例
 如果当前主机的主机名为localhost,则将其修改为www.magedu.com
 #!/bin/bash
 #
 if [ `hostname` == 'localhost' ]; then
  hostname www.magedu.com
 fi
bash中的选择语句if
 在bash中if语句有三种格式:
  1、单分支if语句,语法格式如下:
  单分支的if语句:
   if 测试条件; then
        选择分支
   fi
  当测试条件满足时,执行选择分支语句
  2、双分支的if语句:
   if 测试条件; then
       选择分支1
   else
    选择分支2
   fi
  当测试条件满足时,执行选择分支1,不满足时,执行选择分支2
  示例:
  通过命令行给定一个文件路径,而后判断:如果此文件中存在空白行,则显示其空白行的总数;否则,则显示无空白行;
  #!/bin/bash
  #
  if grep "^[[:space]]*$" $1 &> /dev/null; then
   echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
  else
   echo "No blank lines"
  fi
  3、多分支的if语句:
   if 条件1; then
    分支1
   elif 条件2; then
    分支2
   elif 条件3; then
    分支3
   ...
   else
    分支n
   fi
  示例:
  传递一个用户名给脚本: 如果此用户的id号为0,则显示说这是管理员如果此用户的id号大于等于500,则显示说这是普通用户,否则,则说这是系统用户;
   #!/bin/bash
   #
   if [ $# -lt 1 ]; then
       echo "Usage: `basename $0` username"
       exit 1
   fi

   if ! id -u $1 &> /dev/null; then
       echo "Usage: `basename $0` username"
       echo "No this user $1."
       exit 2
   fi

   if [ $(id -u $1) -eq 0 ]; then
       echo "Admin"
   elif [ $(id -u $1) -ge 500 ]; then
       echo "Common user."
   else
       echo "System user."
   fi

bash条件测试之文件测试:

  -a FILE:文件存在为真,不存在为假
  -e FILE:文件存在为真,不存在为假
  -f FILE:文件存在且为普通文件为真,否则为假
  -d FILE:文件存在且为目录为真,否则为假
  -L/-h FILE:存在并且为符号链接文件,则为真,否则为假
  -b:块设备
  -c:字符设备
  -S:套接字文件
  -p:命名管道
  -s FILE:存在并且为非空文件则为真,否则为假,文件大小不为0
  -r FILE:文件有读取权限为真,否则为假
  -w FILE:文件有写入权限为真,否则为假
  -x FILE:文件有执行权限为真,否则为假
  -N FILE:文件的修改时间要新于访问时间,mtime newer than atime 
  file1 -nt file2:file1的mtime新于file2则为真,否则为假;
  file1 -ot file2:file1的mtime旧于file2则为真,否则为假;

  例如:如果wget命令对应的可执行文件存在且可执行,则使用它下载http://172.16.0.1/centos6.5.repo至当前目录中;

  #!/bin/bash
  #
  downURL='http://172.16.0.1/centos6.5.repo'
  downloader=`which wget`

  if [ -x $downloader ]; then
      $downloader $downURL
  fi

  写一个脚本,完成如下任务:
  1、分别复制/var/log下的文件至/tmp/logs/目录中;
  2、复制目录时,才使用cp -r
  3、复制文件时,使用cp
  4、复制链接文件,使用cp -d
  5、余下的类型,使用cp -a

  #!/bin/bash
  #
  if [ ! -d /tmp/logs ];then
     mkdir /tmp/logs
  fi

  for file in `ls /var/log`;do
   if [ -d /var/log/$file ];then
    cp -r /var/log/$file /tmp/logs
   elif [ -L /var/log/$file ];then
    cp -d /var/log/$file /tmp/logs
   elif [ -f /var/log/$file ];then
    cp /var/log/$file /tmp/logs
   else
    cp -a /var/log/$file /tmp/logs
   fi
  done

 

bash编程之交互编程
 我们编写的脚本有时候需要与用户交互,提示用户输入必要的数据,以便脚本执行下一步的操作,我们可以使用read命令显示一段信息,提示用户键入我们需要的参数
 同时,我们还可以指定等待用户输入超时时间,如果超过指定时间,用户未输入,则自动退出等。
 read命令的语法格式:
  read -p "PROMPT" VAR
  read -t # -p "PROMPT" VAR
 示例:
  输入用户名,可返回其shell
  #!/bin/bash
  #
  read -p "Plz input a username: " userName

  if id $userName &> /dev/null; then
      echo "The shell of $userName is `grep "^$userName\>" /etc/passwd | cut -d: -f7`."
  else
      echo "No such user. stupid."
  fi
  显示一个如下菜单给用户:
  cpu) show cpu infomation
  mem) show memory infomation
  *) quit

  1、如果用户选择了cpu,则显示/proc/cpuinfo文件的内容;
  2、如果用户选择了mem,则显示/proc/meminfo文件的内容;
  3、退出

   #!/bin/bash
   #
   echo "---------menu----------"
   echo "cpu) show cpu infomation"
   echo "mem) show memory infomation"
   echo "*) quit"
   echo "-------menu------------"

   read -p "Plz give your choice: " choice

   if [ "$choice" == 'cpu' ]; then
       cat /proc/cpuinfo
   elif [ "$choice" == 'mem' ]; then
       cat /proc/meminfo
   else
       echo "Quit"
       exit 3
   fi


字串测试中的模式匹配
 有些情况下我们的条件测试中需要使用模糊匹配的功能,比如一个字串是否在一个比它长度大的字符串中存在的情况下,为了达到通配的效果,而不是精确匹配的情况下,我们需要使用到字串测试的模式匹配功能,其语法格式如下:[[ "$var" =~ PATTERN ]]

 示例:
 例如:让用户给定一个用户名,判断其是否拥有可登录shell;
  /bin/sh, /bin/bash, /bin/zsh, /bin/tcsh, /sbin/nologin, /sbin/shutdown


  #!/bin/bash
  #
  read -p "Plz input a username: " userName
  userInfo=`grep "^$userName\>" /etc/passwd`

  if [[ "$userInfo" =~ /bin/.*sh$ ]]; then
      echo "can login"
  else
      echo "cannot login"
  fi

 

bash编程之循环:while 和 until
 while循环的语法格式:
  while 测试条件; do
   循环体
  done
 while循环适用于循环次数未知,或不便用for直接生成较大的列表时;
 而且如果测试结果为“真”,则进入循环;退出条件为,测试条件为假;
 示例:求100以内整数的和
 
  #!/bin/bash
  #
  declare -i count=1
  declare -i sum=0

  while [ $count -le 100 ]; do
      let sum+=$count
      let count++
  done

  echo $sum

 显示当前系统上所有挂载的文件系统中空间使用百分比大于10的设备和空间使用百分比;使用while循环实现;

  #!/bin/bash
  #
  df -P |( while read line;do
  if echo $line | grep -o "\<[[:digit:]]\{1,\}%" > /dev/null;then
   usedspace=`echo $line | grep -o "\<[[:digit:]]\{1,\}%" | cut -d% -f1`
   device=`echo $line | cut -d' ' -f1`
   if [ $usedspace -gt 10 ];then
    echo "$device space has used ${usedspace}%."
   fi
  else
   echo $line &> /dev/null
  fi
  done )

 until循环的语法格式:
 
 until 测试条件; do
   循环体
 done

 如果测试结果为“假”,则进入循环;退出条件为,测试条件为真;
 示例:求100以内所有正整数之和

   #!/bin/bash
   #
   declare -i count=1
   declare -i sum=0

   until [ $count -gt 100 ]; do
       let sum+=$count
       let count++
   done

   echo $sum
 示例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;
     显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止;
     #!/bin/bash
     #
     read -p "Plz enter a username: " userName

     while [ "$userName" != 'q' -a "$userName" != 'quit' ]; do
        if id $userName &> /dev/null; then
            grep "^$userName\>" /etc/passwd | cut -d: -f3,7
        else
           echo "No such user."
        fi

        read -p "Plz enter a username again: " userName
     done

     上面题目使用until循环的写法如下:
      #!/bin/bash
    #

    read -p "Please input a username: " user

    until [ "$user" == 'q' -o "$user" == 'quit' ];do
     if id $user &> /dev/null;then
        userid=`grep "^$user" /etc/passwd | cut -d: -f3`
        usershell=`grep "^$user" /etc/passwd | cut -d: -f7`
        echo "$user id is $userid.shell is $usershell."
     else
        echo "No such user."
     fi
     read -p "Please input a username again: " user
    done

bash中for循环的另外一种格式
 for ((初始条件;测试条件;修改表达式)); do
  循环体
 done
 示例:求100以内所有偶数之和;

  declare -i evensum=0
  for ((counter=2; $counter <= 100; counter+=2)); do
   let evensum+=$counter
  done

bash编程之case语句
 当我们需要做判断的条件情况很多时,如果使用多个if...else语句会显得句子结构不清晰,这个时候,我们可以选择case语句。
 case语句的语法格式:
  case 变量引用 in
  PATTERN1)
   分支1
   ;;
  PATTERN2)
   分支2
   ;;
  ...
  *)
   分支n
   ;;
  esac
 语法格式中的PATTERN类同于文件名通配机制,但支持使用|表示或者;
   a|b: a或者b
   *:匹配任意长度的任意字符
   ?: 匹配任意单个字符
   []: 指定范围内的任意单个字符

 示例:用户键入字符后判断其所属的类别;

  #!/bin/bash
  #

  read -p "Plz enter a char: " char

  case $char in
  [0-9])
      echo "a digit"
      ;;
  [a-z])
      echo "a char"
      ;;
  *)
      echo "a special word"
      ;;
  esac
 示例:
  写一个脚本,使用格式:
  script.sh {start|stop|restart|status}

  1) start: 创建/var/lock/subsys/script.sh
  2) stop: 删除此文件
  3) restart: 先删除文件,再创建文件
  4) status: 如文件存在,显示running,否则,显示stopped

   #!/bin/bash
   #
   srv=`basename $0`

   lockFile="/var/lock/subsys/$srv"

   [ $# -lt 1 ] && echo "Usage: $srv {start|stop|restart|status}" && exit 4

   [ $UID -ne 0 ] && echo "Only root." && exit 5

   case $1 in
   start)
      if [ -f $lockFile ]; then
           echo "$srv is running."
    exit 7
      else
    touch $lockFile
    [ $? -eq 0 ] && echo "Starting $srv OK."
      fi
      ;;
   stop)
       if [ -f $lockFile ]; then
    rm -f $lockFile
    [ $? -eq 0 ] && echo "Stopping $srv OK."
       else
    echo "$srv is stopped yes."
    exit 6
       fi
       ;;
   restart)
       if [ -f $lockFile ];then
         rm -f $lockFile
    [ $? -eq 0 ] && echo "Stopping $srv OK."
       else
    echo "$srv is stopped yet."
       fi
       touch $lockFile
       [ $? -eq 0 ] && echo "Starting $srv OK."
       ;;
   status)
       if [ -f $lockFile ];then
    echo "$srv is running."
       else
    echo "$srv is stopped."
       fi
       ;;
   *)
       echo "Usage: $srv {start|stop|restart|status}" && exit 9
   esac
 写一个脚本,对/etc/目录及内部的所有文件打包压缩
 1、显示一个菜单,让用选择使用的压缩工具:
  xz) xz compress tool
  gz) gzip compress tool
  bz2) bzip2 compress tool
 2、根据用户选择的工具,对/etc执行相应的操作并保存至/backups目录,文件形如/backups/etc-日期时间.tar.压缩后缀

  #!/bin/bash
  #
  [ -d /backups ] || mkdir /backups

  cat << EOF
   xz) xz compress tool
   gz) gzip compress tool
   bz2) bzip2 compress tool
  EOF

  read -p "Plz choose an option: " choice

  case $choice in
  xz)
      tar -Jcf /backups/etc-`date +%F-%H-%M-%S`.tar.xz /etc/*
      ;;
  gz)
      tar -zcf /backups/etc-`date +%F-%H-%M-%S`.tar.gz /etc/*
      ;;
  bz2)
      tar -jcf /backups/etc-`date +%F-%H-%M-%S`.tar.bz2 /etc/*
      ;;
  *)
      echo "wrong option."
      exit 1
      ;;
  esac
 写一个脚本,使用形式如下所示
  showifinfo.sh [-i INTERFACE|-a] [-v]
  要求:
  1、-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;
   显示接口的ip地址
  2、使用-v,则表示显示详细信息
   显示接口的ip地址、子网掩码、广播地址;
  3、默认表示仅使用-a选项;

   #!/bin/bash
   #
   verbose=0
   allInterface=0
   ifflag=0
   interface=0

   while [ $# -ge 1 ]; do
       case $1 in
       -a)
    allInterface=1
           shift 1
    ;;
        -i)
    ifflag=1
    interface="$2"
     shift 2
    ;;
        -v)
    verbose=1
    shift
    ;;
        *)
    echo "wrong option"
    exit 2
    ;;
        esac
   done

   if [ $allInterface -eq 1 ]; then
       if [ $verbose -eq 1 ]; then
    ifconfig | grep "inet addr:"
       else
    ifconfig | grep "inet addr:" | awk '{print $2}'
       fi
   fi

   if [ $ifflag -eq 1 ]; then
       if [ $verbose -eq 1 ]; then
    ifconfig $interface | grep "inet addr:"
       else
    ifconfig $interface | grep "inet addr:" | awk '{print $2}'
       fi
   fi  

 

bash编程函数:
 
 函数可以实现脚本的模块化,可以实现代码重用,可以使脚本结构更加清晰
         函数的语法格式:
          function F_NAME {
           函数体
          }

          F_NAME() {
           函数体
          }
函数可以被调用,调用的方法就是使用函数名,凡是函数名出现的地方,会被自动替换为函数定义的那段代码;

函数的执行会有返回值,一般情况下函数的返回值有以下几种:
       函数的执行结果返回值:代码的输出
       函数中的打印语句:echo, print
       函数中调用的系统命令执行后返回的结果
       执行状态返回值:
       函数体中最后一次执行的命令状态结果
       自定函数执行状态的返回值:return #

函数可以接受参数:在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:如位置参数 $1, $2, ...  $#,$*,$@
 

示例:服务脚本示例

  #!/bin/bash
  #
  # chkconfig: 2345 67 34
  #
  srvName=$(basename $0)

  lockFile=/var/lock/subsys/$srvName

  start() {
      if [ -f $lockFile ];then
   echo "$srvName is already running."
   return 1
      else
   touch $lockFile
   [ $? -eq 0 ] && echo "Starting $srvName OK."
   return 0
       fi
  }

  stop() {
      if [ -f $lockFile ];then
   rm -f $lockFile &> /dev/null
   [ $? -eq 0 ] && echo "Stop $srvName OK" && return 0
      else
   echo "$srvName is not started."
   return 1
      fi
  } 

  status() {
      if [ -f $lockFile ]; then
   echo "$srvName is running."
      else
   echo "$srvName is stopped."
      fi
      return 0
  }

  usage() {
       echo "Usage: $srvName {start|stop|restart|status}"
       return 0
  }

  case $1 in
  start)
   start
   ;;
  stop)
   stop ;;
  restart)
   stop
   start ;;
  status)
   status ;;
  *)
   usage
   exit 1 ;;
  esac

 示例:写一个脚本,完成如下功能(使用函数):
  1、提示用户输入一个可执行命令;
  2、获取这个命令所依赖的所有库文件(使用ldd命令);
  3、复制命令至/mnt/sysroot/对应的目录中
   解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复制到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;
  4、复制各库文件至/mnt/sysroot/对应的目录中;


  #!/bin/bash
  #
  target=/mnt/sysroot/

  [ -d $target ] || mkdir $target

  preCommand() {
      if which $1 &> /dev/null; then
   commandPath=`which --skip-alias $1`
   return 0
      else
   echo "No such command."
   return 1
      fi
  }

  commandCopy() {
      commandDir=`dirname $1`
      [ -d ${target}${commandDir} ] || mkdir -p ${target}${commandDir}
      [ -f ${target}${commandPath} ] || cp $1 ${target}${commandDir}
  }

  libCopy() {
      for lib in `ldd $1 | egrep -o "/[^[:space:]]+"`; do
   libDir=`dirname $lib`
   [ -d ${target}${libDir} ] || mkdir -p ${target}${libDir}
   [ -f ${target}${lib} ] || cp $lib ${target}${libDir}
      done
  }

  read -p "Plz enter a command: " command

  until [ "$command" == 'quit' ]; do

    if preCommand $command &> /dev/null; then
      commandCopy $commandPath
      libCopy $commandPath
    fi

    read -p "Plz enter a command: " command
  done

bash编程之信号捕捉和循环控制

在有些情况下,我们需要中止脚本的运行,需要在脚本运行过程中退出,于是,我们就需要在脚本中为脚本提供一些额外的信号,然后脚本在收到信号以后,做额外的处理动作,比如:
trap 'exit 1' SIGINT;这条语句的效果就是当脚本运行过程中如果我们输入SIGINT信号即Ctrl + c以后,脚本就会执行exit 1操作而退出脚本。

信号捕捉的语法格式:trap 'COMMAND;COMMAND' SIGNAL;kill和term信号是无法捕捉的。

bash循环控制命令:
  continue: continue的作用是提前进入下一轮循环,当一个循环体中遇到continue语句,本次循环就不再执行,无论循环体中是否还有其他语句,而提前进入下一轮循环,它使用于条件语句中,仅在某些个特殊场景提前进入;
  示例:求100以内3的整数倍的和
  #!/bin/bash
  #
  declare -i sum=0

  for i in {1..100};do
      if [ $[$i%3] -ne 0 ];then
          continue
      fi
      let sum+=$i
  echo $sum
  break [n]:在循环体中遇到break语句,就会跳出当前循环,效果是中止当前的循环体,它也是用于条件语句中,如果命令后面跟上数字,可以指定跳出几层循环
  示例:提示用户输入一个用户,显示用户是否登录
  #!/bin/bash
  #
  read -p "Plz enter a username: " userName

  while true; do
      if who | grep "\<$userName\>" &> /dev/null; then
          break
      fi
      echo "not here."
      sleep 5
  done

  echo "$userName is logged on."

bash编程之数组
 当我们需要保存同一类型的变量,且变量的数量很多的情况下,就需要用到数组,数组也是变量,是内存中的存储单元,不过它是连续的多个存储单元,这些存储空间中存储的数据类型是一致的。之前我们引用每个变量时是通过变量名称来引用的,如果把多个变量定义为数组,那么在引用数组内的变量时,可以使用数组名+索引的方式来引用
 数组中索引的表示方式:
   数字索引:在bash中数组的数字索引从0开始定义:a[index]
    如:a[0], a[1]
   关联数组:数组中的元素使用自定义的名称的方式,在引用的时候使用数组名+元素自定义名称
    如:a[hello],a[hi]
 数组的定义方式:
  declare -a ARRAYNAME;定义一个名称为ARRAYNAME的数组
  示例:declare -a a
   a[0]=$RANDOM
   a[1]=$RANDOM
  declare -A ARRAYNAME;定义一个名称为ARRAYNAME的关联数组
  示例:declare -A c
   c[hello]=$RANDOM;
   c[hi]=$RANDOM;

 数组的赋值方式:
  一次只对一个元素赋值:比如,a[0]=$RANDOM,a[1]=$RANDOM,a[2]=$RANDOM...
  一次对所有元素赋值:比如,a=(red green yellow blue)
  使用索引的方式对部分元素进行赋值,bash中支持稀疏格式,可以不定义全部元素的值:比如,a=([0]=green [3]=red [2]=blue [6]=yellow),其中的a[1],a[4],a[5]元素为空
  使用命令引用的方式来赋值:比如,logs=($(ls /var/log/*.log)),效果是将命令ls /var/log/*.log执行结果赋值给数组logs
  使用用户输入的方式来给数组赋值,使用read命令:比如,# read -a A;如果用户输入的是a b c d ;那么在引用时${A[0]}=a ${A[1]}=b ${A[2]}=c ${A[3]}=d

 数组中变量的引用方式:
  索引访问:{a[1]};数组中的元素引用时必须用{}将数组名誉元素索引或者是关联索引扩起来,否则会被bash理解为变量a加上一个[1]字串,所以{}不可省。
  比如:# echo ${a[1]};显示数组a的第二个元素的值

 注:bash中的数组仅支持一维数组

 数组的元素个数即数组的长度计算,有两种方式:
  ${#ARRAY[*]}
  ${#ARRAY[@]}
  上面两种表达式是计算整个数组的元素个数
  ${#ARRAY[1]};这个表达式是计算数组中第二个元素的字符的个数
  ${#ARRAY};这个表达式是计算数组中第一个元素中的字符个数,这个表达式和 $ARRAY的效果相同
 
 示例:写一个脚本,生成10个随机数,保存至数组中,而后显示数组下标为偶数的元素;
 #!/bin/bash
 #
 for i in {0..9};do
  num[$i]=$RANDOM
  if [ $[$i%2] -eq 0 ];then
    echo ${num[$i]}
  fi
 done

 从数组中挑选某元素:
  数组支持切片功能,我们可以使用如下方法取得需要的数组元素
  ${ARRAY[@]:offset:length};其中ARRAY为数组的名称@表示数组中的全部元素,offset为从数组起始元素开始的偏移量,length为需要取的元素的个数
  示例:# A=(a b c d )
     # echo ${A[@]:1:2}
     b c ;取得的结果为b c
     # echo ${A[@]:2}
     c d;冒号后面只有一个数值时,表示偏移量为2,然后取出后面所有数组的值
     # echo ${A[@]};表示取出所有元素
     # echo ${A[@]:0:2};表示取出数组中前面的2个元素

 数组的复制:某些情况下我们需要将某数组的值复制给其他数组,格式如下:array=(${num[@]});这样我们就将数组num的所有元素复制给了新数组array
 示例:复制一个数组中下标为偶数的元素至一个新数组中
   #!/bin/bash
   declare -a mylogs
   logs=(/var/log/*.log)

   echo ${logs[@]}

   for i in `seq 0 ${#logs[@]}`; do
       if [ $[$i%2] -eq 0 ];then
          index=${#mylogs[@]}
          mylogs[$index]=${logs[$i]}
       fi
   done

   echo ${mylogs[@]}

 从数组中删除元素:unset ARRAY[index]
 示例:数组logs=(/var/log/*.log),删除其中的第0个元素
 # unset logs[0]

 

bash编程之字串处理:bash中的字串处理与取数组中连续的某些元素功能相似,是取出字符串中连续的部分子串,其语法如下:
 ${string:offset:long}
 示例:
 # string='hello world'
 # echo ${string:2:4}
 # echo ${string:2:5}
 # echo ${string:2}
 # echo ${string: -2};从右向左,取后面的2个字符

 字串处理可以基于模式取子串
 示例:
 # echo ${variable#word};在variable中存储字串上,自左而右,查找第一次出现word,删除字符开始至此word处的所有内容;
 # echo ${variable##word};在variable中存储字串上,自左而右,查找最后一次出现word,删除字符开始至此word处的所有内容;
 # file=/var/log/messages
 # echo ${file#/*e};结果是输出ssages
 # echo #{file##*e};结果输出是一个字母s
 # echo ${file#*/};返回结果是var/log/messages
 # echo ${file##*/};返回messages,效果同取基名

 # echo ${variable%word*}:在variable中存储的字串上,自右而左,查找第一次出现word,删除此word处至字串尾部的所有内容;
 # echo ${variable%%word*}:在variable中存储的字串上,自右而左,查找最后最后一次出现的word,删除此word处至字串尾部的所有内容;

 file='/var/log/messages'
 # echo ${file%/*};返回的结果是/var/log
 # echo ${file%%/*};返回的结果是空

 phonenumber='010-110'
 # echo ${phonenumber%%-*};取区号
 # echo ${phonenumber%-*};取电话号码
 phonenumber='010-110-8'
 # echo ${phonenumber##*-};取接线员的号码,只显示8

 url="http://www.magedu.com"
 # echo ${url##*/};取主机名
 url="http://www.magedu.com:80"
 # echo ${url%:*};取端口号

字串的查找替换
 # tail -1 /etc/passwd | sed "s@fedora@centos@g";将最后一行中的fedora替换成centos
 # userinfo=`tail -1 /etc/passwd`
 # echo $userinfo
 # echo ${userinfo/fedora/centos};将/etc/passwd文件中最后一行中的第一个fedora字符串替换成centos
 # echo ${userinfo//fedora/centos};将/etc/passwd文件中最后一行里面的所有fedora字符串替换成centos

查找替换的语法格式:
 ${variable/pattern/substi};替换一个字串
 ${variable//pattern/substi};替换所有的出现
 # echo ${userinfo/#fedora/centos};位置锚定的效果,锚定行首的fedora,再使用centos替换
 # useradd -d /home/test hello
 # userin=`tail -1 /etc/passwd`
 # echo $userin
 # echo ${userin/#test/TEST};这个语句的意思是替换行首的test换成大写的TEST,但是之前添加的用户是hello,家目录是test,这个test在行的中间位置,所以命令执行的时候是不会替换的
 # echo ${userin/test/TEST};这个操作会替换行中出现的test成TEST
 # useradd bash
 # user=`tail -1 /etc/passwd`
 # echo ${user/#bash/BASH};替换行首的bash为BASH
 # echo ${user/bash/BASH};替换行中第一个字串bash为BASH
 # echo ${user//bash/BASH};替换行中多个字串bash为BASH
 # echo ${user/%bash/BASH};替换行尾的bash为BASH

字符串的查找删除操作:
 # echo ${variable/pattern}
 # echo ${user/bash};删除user变量中第一个bash字符串
 # echo ${user//bash};删除user变量中全部bash字符串
 # echo ${user/#bash};删除user变量中行首的bash
 # echo ${user/%bash};删除user变量中行尾的bash

字符串的大小写转换:
 小-->大:${variable^^}
 大-->小:${variable,,}

 # echo ${user^^}
 # echo ${user,,}

 注:替换被模式匹配到的字串替换大小写,这种操作只能改变变量中的单个字符,将单个字符大小写改变
 示例:
 # echo ${user^^s};可以将user变量中的字母s改变成大写,不支持更改字符串的大小写。

变量赋值操作:

${parameter:-word}:如果parameter这个变量没有被设定或赋值,那么整个表达式的值会被设置为word,而parameter的值不会发生变化,还是为空,如果有值,那么就显示原有的值

# url='www.magedu.com'
# echo ${url:-mageedu.com};返回的结果是www.magedu.com
# unset url
# echo ${url:-mageedu.com};返回的结果是mageedu.com
# echo $url;这时候显示的结果为空

${parameter:=word}:如果parameter这个变量没有被设定或赋值,那么整个表达式的值会被设置为word,而且parameter的值会被赋值为word,如果parameter有值,那么表达式的值是parameter的值,这个赋值表达式有赋默认值的效果

# url='www.magedu.com'
# echo ${url:=mageedu.com};返回的值是www.magedu.com
# unset url
# echo ${url:=mageedu.com};此时返回的值是mageedu.com
# echo $url;此时url这个变量被赋值为mageedu.com,变量赋值的效果

${parameter:+word}:如果parameter变量没有被设定或赋值,那么整个表达式就不被赋值,显示的时候会是空,如果parameter有初始值,那么整个表达式的值就使用后面的word来显示,这个应用的场景是某个变量的值作为标志位,如果标志位为什么值,那么就启用什么特性。如果这个标志位没有,那么就把它的特性关闭,比如在脚本中使用DEBUG功能,如果这个DEBUG变量的值设为1,那么后面的选项就有效,如果DEBUG变量的值没有设置或者为空,那么后面的选项就无效了。

${parameter:?word}:如果parameter变了没有被设定或赋值,那么整个表达式在被调用时会显示word字串的信息,可以作为命令的错误提示信息使用
示例:
# debug=1
# echo ${debug:+verbose};当debug的值为1时,整个表达式的值就是verbose
# unset debug
# echo ${debug:+verbose};没有设置debug的值,整个表达式的值就为空
# echo ${debug:?null};当debug没有值时,会显示-bash:debug:null
# echo $?;返回的值也是非0值


为脚本使用配置文件,并确保其变量有可用值的方式
 variable=${variable:-default value}

脚本示例:读取/etc/sysconfig/network文件,利用其HOSTNAME变量的值设置主机名;
 #!/bin/bash
 #
 [ -f /etc/sysconfig/network ] && source /etc/sysconfig/network

 host=${HOSTNAME:-www.magedu.com}

 hostname $host

 hostname