bash脚本编程的所有语法知识点总结第一部分

名词概念

bash脚本编程,从字面意思理解也就是shell脚本编程。bash应该是目前Linux上最流行的shell脚本解释程序。shell是一个命令语言解释器,而shell脚本则是Linux命令的集合,按照预设的顺序依次解释执行,来完成特定的、较复杂的系统管理任务,类似于Windows中的批处理文件。

1.shell脚本格式

    第一行,顶格。

    eg:

    #!/bin/bash

    #!/usr/bin/python

   

    其它的以#开头的行均为注释,会被解释器忽略;

   

    执行方式:

    bash编程属于面向过程编程,执行方式如下:

    顺序执行:按命令先后顺寻依次执行

    选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支

    循环执行:将同一段代码反复执行多次,因此,循环必须有退出条件;否则,则陷入死循环

   

    执行相关选项

    bash-n SHELLNAME 

#语法测试,测试是否存在语法错误

bash -x SHELLNAME

#模拟单步执行,显示每一步执行过程

 

2.bash的重要特性变量

2.1.bash变量类别

2.1.1.本地变量

    只对当前shell进程有效的变量;对其它shell进程无效,包当前shell进程的子进程也无效;

        VAR_NAME=VALUE

        变量赋值:向变量的存储空间保存数据

        变量引用:${VAR_NAME}或者反单引号``

    bash中,其用法同$()一样,用``括起来的内容代表一个变量,例如 echo `ls`

    根据POSIX规范,要求系统工程师采用的是$(命令)的形式。所以,我们最好还是遵循这个规范,少用``,多用$()

        "":弱引用,里面的变量会被替换;

        '':强引用,里面的所有字符都是字面量,直接输出;

    eg: echo “$name”   jerry

        echo ‘$name’   $name

2.1.2.环境变量

    对当前shell进程及其子shell有效,对其它的shell进程无效;

     定义:export VAR_NAME=VALUE

     导出:export VAR_NAME

 

     用户可自义环境变量

     bash有许多内置的环境变量

 

     撤消变量:unset VAR_NAME

     只读变量:readonly VAR_NAME

 

2.1.3.局部变量

   对shell脚本中某代码片断有效;通常用于函数本地;

    local VAR_NAME=VALUE

 

2.1.4.位置变量

    用来接受变量指定位置的参数

     $1, $2, ..., ${10}

 

2.1.5.特殊变量

    shell对一些参数做特殊处理,这些参数只能被引用而不能被赋值

    $#  传递到脚本的参数个数

    $*显示所有向脚本传递的参数                            #与位置变量不同,此选项参数可超过9个

    $$  获取当前shell的进程号

    $!  执行上一个指令的进程号

    $?  获取执行的上一个指令的返回值                        #0为执行成功,非零为执行失败

    $-  显示shell使用的当前选项,与set命令功能相同

    $@  与$*相同,但是使用时加引号,并在引号中返回每个参数

 

2.2.变量查看

    查看当前shell进程中的所有变量:set

     查看当前shell进程中的所有环境变量:export,printenv, env

 

2.3变量命名 

    a、不能使用程序中的关键字(保留字);

         eg:if, case, for

     b、只能使用数字、字母和下划线,且不能以数字开头;

     c、要见名知义

 

2.4变量类型

    数值型:

          精确数值:整数

          近似数值:浮点型

             单精度浮点

             双精度浮点

     字符型:char,string

     布尔型:true, false

 

     类型转换:显式转换,隐式转换

bash是弱类型的语言:一切皆字符

2.5bash的配置文件

2.5.1 profile

    为交互式登录的用户提供配置

     全局:/etc/profile   /etc/profile.d/*.sh 配置文件片段

     用户:~/.bash_profile

 

     功用:

         1、设定环境变量

         2、运行命令或脚本

 

2.5.2 bashrc

    为非交互式的用户(后台运行,非用户登录)提供配置

     全局:/etc/bashrc

     用户:~/.bashrc

 

     功用:

         1、设定本地变量

         2、定义命令别名

 

2.5.3登录类型

     交互式:

          直接通过终端输入账号和密码登录;

          使用su -l USERNAME 或 su -USERNAME;

 

     非交互式:

          su USERNAME

          图形界面下打开的终端

          执行脚本

 

     通过编辑配置文件修改的配置生效

         1、退出并重新登录;

         2、让bash重读此配置文件;

          . FILE

          source FILE

 

    用户登录时读取配置文件,越先读取越被先覆盖,有越后读取越有可能最终生效

     交互登录的用户:

     /etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile--> ~/.bashrc --> /etc/bashrc(最终生效的)

 

     非交互登录的用户:

     ~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh

    

3.bash编程之循环语句

    for:遍历有限的元素列表,

     while

     until

    

3.1循环之for循环

3.1.1 for语句格式一

        for VAR_NAME in LIST

        do

          循环体

        done

 

    列表:可包含一个或多个元素

    循环体:依赖于调用变量来实现其变化

    循环可嵌套

    退出条件:遍历元素列表结束

 

    生成数值列表:

             {start..end}

               例如:{1..100}

 

    seq命令引用

    eg:seq 10      1到10

       seq 1 10    1到10

       seq 2 2 10  2 4 6 8 10 每次进阶2

    {301..310} 等价于${seq 301 310}   `seq 301 310`

    练习:创建10个用户,user301, user310

          列表:user301,user310

          列表:301, 310

          for userNo in {301..310}

          for userNo in $(seq 301 310); do

              useradduser${userNo} // user$userNo

          done

 

    练习:创建目录/tmp/dir-当前日期时间;例如/tmp/dir-20140707-155503在此目录中创建10个空文件,分别为file1-file10;

    #!/bin/bash

    dstDir=/tmp/-$(date +%Y%m%d-%H%M%S)

    mkdir $dstDir

    for i in {1..10};do

    touch $dstDir/file$i

    done

 

    练习:写一个脚本

    1、创建用户tuser1-tuser9;

    2、创建目录/tmp/dir-当前日期时间;

    3、在/tmp/dir-当前日期时间 目录中创建9个空文件file101-file109

    4、将file101的属主改为tuser1,依次类推,一直将file109的属主改为tuser9;

   

    #!/bin/bash

    dstDir=/tmp/-$(date +%Y%m%d-%H%M%S)

    mkdir $dstDir

    for i in {1..9};do

    useradd tsuer$i

    touch $dstDir/file10$i

    chown tuser$i $dstDir/file10$i

    done

 

     练习:写一个脚本

     1、脚本可以接受一个以上的文件路径作为参数;

     2、显示每个文件所拥的行数;

     3、显示本次共对多少个文件执行了行数统计;

     !/bin/bash

     #

     for file in $*; do

         lines=`wc -l $file |cut -d' ' -f1`

         echo "$file has$lines lines."

     done

 

     echo "$# files."

 

 

    练习:写一个脚本

     显示/etc/passwd文件中位于文件的第偶数行的用户名;并显示共有多少个这样的用户;

          #!/bin/bash

          #

          totalUsers=`wc -l /etc/passwd | cut -d' ' -f1`

 

          for i in `seq 2 2 $totalUsers`; do

              userName=`head -n$i /etc/passwd | tail -1 | cut -d: -f1`

              echo $userName>> /tmp/passwd.tmp

              echo $userName

          done

 

          users=`wc -l /tmp/passwd.tmp | cut -d' ' -f1`

           echo "Totalusers: $users."     

 

     生成列表的方式:

        1、手动给个列表:

          for i in 1 2 3 4 5; for i in{1..5};

        2、数值列表:

          {start..end}

          `seq [start [increment]] end`

        3、$*, $@

        4、命令生成列表

 

3.1.2 for的第二种使用格式

     for ((初始条件;测试条件;修改表达式)); do

        循环体

     Done

 

    先用初始条件和测试条件做判断,如果符合测试条件则执行循环体,再修改表达式,否则直接跳出循环。

  

    eg:求100以内所有正整数之和:

        while的实现:

          #!/bin/bash

          declare -i counter=1

          declare -i sum=0

 

          while [ $counter -le 100 ]; do

             let sum+=$counter

             let counter++

          done

 

          echo $sum

 

        for的实现:变量赋值时都不能加$,比较时可以

          #!/bin/bash

          #

          declare -i sum=0

 

          for ((counter=1;$counter <= 100; counter++)); do

             let sum+=$counter

          done

 

          echo $sum

 

        练习:求100以内所有偶数之和;

 

          declare -i evensum=0

          for ((counter=2; $counter <=100; counter+=2)); do

             let evensum+=$counter

          done

 

3.2 循环之while循环

 

3.2.1 循环之while语句

   while格式:

     while 测试条件; do

        循环体

     done

 

    测试条件为真,进入循环;测试条件为假,退出循环;

    测试条件一般通过变量来描述,需要在循环体变量地改变变量的值,以确保某一时刻测试条件为假,进而结束循环;

 

    例:求100以内所有偶数之和,要求使用取模方法

    #!/bin/bash

    declare -i counter=1

    declare -i sum=0

    while [ $counter -le 100 ]; do

        if [ $[$counter%2] -eq 0 ]; then

             let sum+=$counter

        fi

        let counter++

    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

 

3.2.2 while特殊用法:遍历文本文件

     格式:

     while read 变量名; do

        循环体

     done < /path/to/somefile

 

     变量名,每循环一次,记忆了文件中一行文本

 

  练习:显示其ID号为偶数的用户的用户名、ID号和SHELL

        #!/bin/bash

        while read line; do

            userID=`echo $line| cut -d: -f3`

            if [ $[$userID%2]-eq 0 ];then

                echo $line |cut -d: -f1,3,7

            fi

        done < /etc/passwd

 

     练习:显示ID号为偶数,且ID号同GID的用户的用户名、ID和SHELL;

     while read line; do

        userID=`echo $line | cut -d: -f3`

        groupID=`echo $line | cut -d: -f4`

        if [ $[$userID%2] -eq 0 -a $userID -eq $groupID ]; then

                echo $line |cut -d: -f1,3,7

            fi

        done < /etc/passwd    

 

3.3 循环之until语句

语句结构:

until 测试条件; do

      循环体

done

测试条件为假,进入循环;测试条件为真,退出循环

例:求100以内所有偶数之和,要求使用取模方法(until实现)

#!/bin/bash

declare -i counter=1

declare -i sum=0

until [ $counter -gt 100 ];do

    if [ $[$counter%2] -eq 0 ]; then

         let sum+=$counter

    fi

    let counter++

done

echo $sum

 

例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止(until实现)

#!/bin/bash

read -p "Plz enter ausername: " userName

until [ "$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 ausername again: " userName

done

 

3.4 循环之循环控制和shift

    break:提前退出循环

    break [N]: 退出N层循环;N省略时表示退出break语句所在的循环,用于条件语句中

    continue: 提前结束本轮循环,而直接进入下轮循环

    用于条件语句中,仅在某些个特殊场景提前进入;

    continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环

    死循环:

    

    #while

    while true; do

          循环体

    done

    #until

    until false; do

          循环体

    done

 

     例如:写一个脚本,判断给定的用户是否登录了当前系统

    (1) 如果登录了,则脚本终止;

    (2) 每5秒种,查看一次用户是否登录;

    eg1:while循环实现

        #!/bin/bash

        read -p "Plz enter a username: " username

 

        while true; do

            if who | grep"\<$userName\>" &> /dev/null; then

                break

            fi

            echo "nothere."

            sleep 5

        done

        echo "$userName is logged on."

       

    eg1:until循环实现

        #!/bin/bash

        #

        read -p "Plz enter a username: " userName

 

        until who | grep "\<$userName\>" &>/dev/null; do

          sleep 5

          echo "nothere"

        done

 

        echo "here"

    如果没有数字,只有shift 就是跳过一个参数获取下一个参数,如果加上数字,比如shift 2 ,跳过两个参数获取下一个参数。

   

    例:写一个脚本,使用形式如下所示   

        showifinfo.sh [-i INTERFACE|-a] [-v]

        要求:

        1、-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;

        显示接口的ip地址

        2、使用-v,则表示显示详细信息

        显示接口的ip地址、子网掩码、广播地址;

        3、默认表示仅使用-a选项;

        #!/bin/bash

        allinterface=0

        ifflag=0

        verbose=0

        interface=0

        if [ $# -eq 0 ];then

            ifconfig | grep "inetaddr:" | awk '{print $2}'

        fi

        while [ $# -ge 1 ];do

          case $1 in

          -a)

            allinterface=1

            shift 1

          ;;

          -i)

            ifflag=1

            interface=$2

            shift 2

          ;;

          -v)

            verbose=1

            shift 1

          ;;

          *)

            echo "error option"

            exit 2

          ;;

          esac

        done

        if [ $allinterface -eq 1 ];then

          if [ $ifflag -eq 1 ];then

              echo "command notfound"

              exit 5

          fi

          if [ $verbose -eq 1 ];then

              ifconfig | grep "inetaddr:"

          else

              ifconfig | grep "inetaddr:" | awk '{print $2}'

          fi

        fi

        if [ $ifflag -eq 1 ];then

          if [ $allinterface -eq 1 ];then

                echo "command notfound"

                exit 5

          fi

          if [ $verbose -eq 1 ];then

              ifconfig $interface | grep"inet addr:"

          else

              ifconfig $interface | grep"inet addr:" | awk '{print $2}'

          fi

        fi

 

4.bash之算数运算与逻辑运算

4.1算数运算

4.1.1 变量赋值

    变量:弱类型

    

    变量=值

     任何无需事先声明,可直接使用

     值默认都是字符型

     a=abc, b=3

     a=3  给a指向的内存空间赋值

     赋值:

     a=4

     增强型赋值:

     +=, -=, *=, /=, %=

     a=$[$a+1] 相当于 let a+=1

    

     自加:var++, var--, ++var, --var

     export PATH=$PATH:/usr/local/apache/bin 字串的增强型赋值

     unset 撤消   

      

    算术运算:bash会对数字执行隐式的类型转换

     let VAR_NAME=Integer_Value

     declare -i Var_Name=Integer_Value (-I 指明整型数值)

    存储为数值和ASCII是不同的,eg:121 分别为1个3个字节

 

     如何定义整型变量:

          letVAR_NAME=INTEGER_VALUE

          例如:let a=3

   

        declare -i VAR_NAME=INTEGER_VALUE

          例如:declare-i a=3

 

     注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算;bash会执行变量类型的隐式类型转换;

 

4.1.2 bash实现算术运算的方式

     letVAR_NAME=ARITHMATIC_EXPRESSION 完整语句

     VAR_NAME=$[ARITHMATIC_EXRESSION]表达式

     VAR_NAME=$((EXPRESSION))  表达式,推荐方式

     VAR_NAME=$(expr $num1 + $num2)

     注意:表达式$num1,$num2和+号间有空格

        命令:expr ARG1 OP ARG2

 

   如果计算结果中存在小数,都会被圆整,即舍去小数部分

    算术运算符:+,-,*,/,%:取模,取余数

            +=,-=,*=,/=,%=

    双目运算符:需要至少两个操作数

    totalweight=$[ $totalweight+2 ]

    lettotalweight+=2

    let totalweight++ 相当于totalweight+=1

 

    练习:计算100以内所有正整数之和

        #!/bin/bash

        #

        declare -i sum=0

 

        for i in {1..100}; do

          sum=$[$sum+$i] //或者$sum+=$i

        done

 

        echo $sum

 

    练习:分别计算100以内所有偶数之和和奇数之和;

        #!/bin/bash

        #

        declare -i evensum=0

        declare -i oddsum=0

 

        for i in `seq 1 2 100`; do

          oddsum=$[$oddsum+$i]

        done

 

        for j in `seq 2 2 100`; do

         evensum=$[$evensum+$j]

        done

 

        echo "evensum: $evensum, oddsum: $oddsum."

 

 

    练习:计算当前系统上所有用户的ID之和;

        declare -i idsum=0

 

        for i in `cut -d: -f3 /etc/passwd`; do

            let idsum+=$i

        done

 

        echo $idsum

 

    练习:计算/etc/rc.d/rc.sysinit、/etc/rc.d/init.d/functions和/etc/issue三个文件的字符数之和

   #!/bin/bansh

    declare –i bytesCount=0

    for file in etc/rc.d/rc.sysinit/etc/rc.d/init.d/functions  /etc/issue;do

    let bytesCount=$bytesCount+`wc –c $file|cut–d' ' –f1`

    done

    echo $ bytesCount

 

    练习:新建10个用户tuser401-tuser410,并求他们的ID之和;

        #!/bin/bash

        #

        declare -i idsum=0

 

        for i in {401..410}; do

            useradd tuser$i

            userID=`id -u tuser$i`

           let idsum+=$userID

        done

 

        echo "ID sum: $idsum."

 

     练习:写一个脚本

        1、脚本可以接受一个以上的文件路径作为参数;

        2、显示每个文件所拥有的行数;

        3、显示本次共对多少个文件执行了行数统计;

        4、显示所有文件的总行数;

 

          #!/bin/bash

          #

          declare -i totalLines=0

          declare -i noFiles=0

 

          for file in $*; do

              curFileLines=`wc-l $file | cut -d' ' -f1`

              echo "$filehas $curFileLines."

              let noFiles++

              lettotalLines+=$curFileLines

          done

 

          echo "Total Files: $noFiles."

          echo "Total Lines: $totalLines."

 

     练习:写一个脚本

        分别统计/etc/rc.d/rc.sysinit、/etc/rc.d/init.d/functions和/etc/inittab文件中以#开头的行的行数和空白行数;

       

          #!/bin/bash

 

          for file in /etc/rc.d/rc.sysinit/etc/rc.d/init.d/functions  etc/inittab;do

              echo "Thelines contain #  in $file is `grep -E"^#" $file | wc -l`."

              echo "Thespace lines in $file is `grep -E "^[[:space:]]*$" $file | wc-l`."

          done

 

     练习:写一个脚本

        显示当前系统上所有默认shell为bash的用户的用户名、UID及其所有此类用户的UID之和;

          #!/bin/bash

          #

          grep "/bin/bash$" /etc/passwd | cut -d: -f1,3

 

          declare -i sum=0

          for userID in `grep "/bin/bash$" /etc/passwd |cut -d: -f3`; do

             let sum+=$userID

          done

 

          echo "$sum"

 

    练习:写一个脚本,显示当前系统上有附加组的用户的用户名;并统计共有多少个此类用户;

    for userName in `cut -d: -f1 /etc/passwd`;do

     id $userName |

     # egrep '[^:]$' /etc/group | cut -d: -f4 | sort -u | egrep -o'[[:alnum:]]*' | sort -u

 

4.2 逻辑运算

4.2.1 逻辑运算定义

    实现某种操作:总是测试前提是否满足

 

     逻辑运算:

     布尔运算:真,假

 

     与、或、非、异或

        与运算:

        真,假:

          真 &&真 = 真

          真 &&假 = 假

          假 &&真 = 假

          假 &&假 = 假

 

     或运算:

        真,假

          真 || 真 = 真

          真 || 假 = 真

          假 || 真 = 真

          假 || 假 = 假

 

     非运算:

        真,假

 

     异或运算:

    a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0

 

     命令都有其状态返回值:

     成功:0,真

     失败:1-255, 假

 

4.2.2 bash条件测试

4.2.2.1 条件测试定义

     命令执行成功与否即为条件测试

        test EXPR

        [ EXPR ] 表达式和中括号中间要有空格

        ` EXPR ` 表达式和中括号中间要有空格

    eg: test $num1 > $num2

        echo $?

      比较运算:

      >, <, >=, <=,==, !=

 

4.2.2.2 测试类型

根据比较时的操作数的类型

        整型测试:整数比较

        字符测试:字符串比较

        文件测试:判断文件的存在性及属性等

 

        注意:比较运算通常只在同一种类型间进行

 

4.2.2.3 整型测试

          -gt(greater): 例如 [ $num1 -gt $num2 ]

          -lt(less小于):

          -ge(大于等于):

          -le(小于等于):

          -eq(等于):

          -ne(不等于):

 

4.2.2.4 字符串测试

          双目

             >: [[ "$str1" > "$str2"]]

             <:

             >=

             <=

             ==

             !=

 

     单目:

     -n String: 是否不空,不空则为真,空则为假

     -z String: 是否为空,空则为真,不空则假

 

5. bash的选择语句if

5.1 单分支的if语句

     if 测试条件;then

           选择分支

     fi

     表示条件测试状态返回值为值,则执行选择分支;

    

    eg:

          if ! id $username &> /dev/null; then

             useradd $username

          fi

   

    练习:写一个脚本,接受一个参数,这个参数是用户名;如果此用户存在,则显示其ID号;

    if [ $# -lt 1 ];then

      echo “Usage:  `basename $0` username”

      exit 2

    fi

    if id $1 &> /dev/null;then

         userid=`id –u $1`

         echo “$1 id is $userid”

    else

      echo “No this user:$1. ”

    fi

 

    写脚本的时候有两个易犯错的地方:

    1.if [ $# -lt 1 ]  if与[  $#与-lt中间都必须带空格

    2.id $1 &> /dev/null;这样的只是取命令的返回值不是进行算是运算,if条件时不用加[]

 

5.2 双分支的if语句

     if 测试条件; then

          选择分支1

     else

          选择分支2

     fi

 

    两个分支仅执行其中之一。

 

    练习:通过命令行传递两个整数参数给脚本,脚本可以返回其大者。

    if [ $1 -gt $2 ];then

      echo “The max num is  $1.”

    else

      echo “The max num is  $2.”

    fi

 

    练习:通过命令行传递任意个整数给脚本,脚本可以返回其大者。

     Declare –i max=0

    for i in $*;do

      if [ $max –lt $i ];then

          max=$i

    fi

    done

 

    练习:通过命令行给定一个文件路径,而后判断:如果此文件中存在空白行,则显示其空白行的总数;否则,则显示无空白行;

   

    if grep"^[[:space:]]*$" $1 &> /dev/null; then

        echo "$1 has $(grep"^[[:space]]*$" $1 | wc -l) blank lines."

    else

        echo"No blank lines"

    fi

   

    注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用;而如果把命令的执行结果与某个值做比较,则需要引用了

   

    if [$(grep "^[[:space:]]*$" $1 | wc -l) -lt 1 ]

   

5.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

   

    if 测试条件; then

    测试条件:在bash中是命令 (test EXPR, [ EXPR ] ) 或由 ` EXPR `

    if 命令;

    在bash运行至if时,其后的命令会被执行,其状态结果则作为判断标准:

        0:    表示真 $?为0

        1-255:表示假

   

    如果条件包含比较之意,则必须使用(test EXPR, [ EXPR ] ) 或由 ` EXPR `

   

   

    自定义shell进程的状态返回值:

     exit[n]

   

    回顾:

     条件测试:

        整型测试:数值间的大小比较

           -gt, -lt, -eq, -ne, -ge, -le

        字符串测试:字符串大小比较

           >,<, ==, !=, =~, -n, -z

        文件测试

     例如:如果当前主机的主机名为localhost,则将其修改为www.magedu.com

   

        比较时,

        if[ `hostname` == 'localhost' ]; then

           hostnamewww.magedu.com

        fi

   

    进行比较时若变量有可能为空,则进行弱引用[ "$str3" == 'localhost' ]

   

   

    例如:如果当前主机的主机名为空,则将其修改为用户通过命令行参数传递过来的用户名

        hostName=`hostname`//保存下当前主机名

   

        if[ -z "$hostName" ]; then

           hostname$1

        fi

   

   

5.4 组合条件测试

    在多个条件间实现逻辑运算

        与:[ condition1 -a condition2 ]

           condition1&& condition2

        或:[ condition1 -o condition2 ]

           condition1|| condition2

        非:[ -not condition ]

           !condition

   

   

    例如:如果当前主机的主机名为空,或者为"localhost",则将其修改为www.magedu.com

     #!/bin/bash

     #

     hostName=`hostname`

   

     if [-z "$hostName" -o "$hostName" == 'localhost' ]; then

         hostname www.magedu.com

   

    例如:如果某用户存在,且answer变量的值为“yes",则显示用户的ID号;否则,说用户选择了退出;

           id$userName

           retVal=$?

   

           if[ $retval -eq 0 -a "$answer" == 'yes' ]; then

   

           上述方式改为:

   

           ifid $userName &> /dev/null && [ "$answer" =='yes' ];then

   

    练习:给定一个用户,如果其shell为/bin/bash且其ID号大于等于500,则说这是一个可登录普通用户;否则,则显示其为非登录用户或管理员。

        #!/bin/bash

        #

        if! id $1 &> /dev/null; then

            echo "No this user."

            exit 3

        fi

   

        userShell=$(grep"^$1\>" /etc/passwd | cut -d: -f7)

        userID=$(id-u $1)

   

        if[ "$userShell" == '/bin/bash' -a $userID -ge 500 ]; then

            echo "Login user."

        else

            echo "not login user."

        fi  

   

    练习:给定一个用户,如果其shell为/bin/bash且其ID号大于等于500,则说这是一个可登录普通用户;否则,则显示其为非登录用户或管理员。

        #!/bin/bash

        #

        if! id $1 &> /dev/null; then

            echo "No this user."

            exit 3

        fi   

        userShell=$(grep"^$1\>" /etc/passwd | cut -d: -f7)

        userID=$(id-u $1)

   

        if[ "$userShell" == '/bin/bash' -a $userID -ge 500 ]; then

            echo "Login user."

        else

            echo "not login user."

        fi   

   

    练习:写一个脚本

        如果某用户不存在,就添加之;

           #!/bin/bash

           #

           if! id $1 &> /dev/null; then

               useradd $1

           fi  

   

    练习:写一脚本

        1、添加10个用户:tuser501-tuser510

           如果用户不存在,才添加;如果存在,则显示已经有此用户

        2、显示一共添加了多少个用户;

           #!/bin/bash

           #

           declare-i count=0

   

           fori in {501..510}; do

               if id tuser$i &> /dev/null; then

                   echo "tuser$i exists."

               else

                   useradd tuser$i

                   let count++

               fi

           done

   

           echo"Total add $count users."

   

   

    练习:写一脚本

        1、添加10个用户:tuser601-tuser610

           如果用户不存在,才添加,并以绿色显示添加成功;如果存在,则以红色显示已经有此用户;

        2、显示一共添加了多少个用户;

           #!/bin/bash

           #

           declare-i count=0

   

           fori in {501..510}; do

               if id tuser$i &> /dev/null; then

                   echo -e "\033[31mtuser$i\033[0mexists."

               else

                   useradd tuser$i

                   echo -e "add user\033[32mtuser$i\033[0m successfully."

                   let count++

               fi

           done

   

           echo"Total add $count users."

   

   

    练习:写一个脚本

        传递用户名给脚本

        1、判断此用户的shell是否为/bin/bash,如果是,则显示此用户为basher

        2、否则,则显示此用户为非basher

           #!/bin/bash

           #

           userShell=`grep"^$1\>" /etc/passwd | cut -d: -f7`

   

           if[ "$userShell" == '/bin/bash' ]; then

               echo "basher"

           else

               echo "not basher"

           fi  

   

 

5.5 bash条件测试之文件测试

    判断文件的存在性及属性等,判定语句需放在[]中

    -aFILE:存在则为真;否则则为假;

    -eFILE: 存在则为真;否则则为假;

    -fFILE: 存在并且为普通文件,则为真;否则为假;

    -d FILE: 存在并且为目录文件,则为真;否则为假;

    -L/-h FILE: 存在并且为符号链接文件,则为真;否则为假;

    -b: 存在并且为块设备,则为真;否则为假;

    -c: 存在并且为字符设备,则为真;否则为假

    -S: 存在并且为套接字文件,则为真;否则为假

    -p: 存在并且为命名管道,则为真;否则为假

    -sFILE: 存在并且为非空文件则为真,否则为假;

    -r FILE:文件可读为真,否则为假

    -w FILE:文件可写为真,否则为假

    -x FILE:文件可执行为真,否则为假

 

    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、判断此文件是否存在;不存在,则说明文件不存,并直接结束脚本;

     2、如果文件是否普通文件,则显示为“regularfile”;

        如果文件是目录,则显示为“directory”;

        如果文件是链接文件,则显示为“Symbolic file";

        否则,则显示为“unknown type.”

 

        #!/bin/bash

        #

        if [ ! -e $1 ]; then

            echo "file notexist."

            exit 8

        fi

 

        if [ -L $1 ]; then

            echo "Symbolicfile"

        elif [ -d $1 ]; then

            echo"Directory"

        elif [ -f $1 ]; then

            echo "regularfile."

        else

            echo"unknown."

        Fi

 

   写一个脚本,完成如下任务,其使用形式如下所示:

     script.sh {start|stop|restart|status}

   其中:

     如果参数为空,则显示帮助信息,并退出脚本;

     如果参数为start,则创建空文件/var/lock/subsys/script,并显示“starting scriptsuccessfully.”

     如果参数为stop,则删除文件/var/lock/subsys/script,并显示“Stop scriptsuccessfully.”

     如果参数为restart,则删除文件/var/locksubsys/script并重新创建,而后显示“Restartingscript successfully.”

     如果参数为status,那么:

        如果文件/var/lock/subsys/script存在,则显示“Script is running...”,否则,则显示“Script isstopped.”

        #!/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

 

        if [ "$1" == 'start' ]; then

           if [ -f $lockFile ];then

                echo "$srvis running."

                exit 7

           else

                touch $lockFile

                [ $? -eq 0 ]&& echo "Starting $srv OK."

           fi

        elif [ "$1" == 'stop' ]; then

            if [ -f $lockFile]; then

                rm -f $lockFile

                [ $? -eq 0 ]&& echo "Stopping $srv OK."

            else

                echo "$srvis stopped yes."

                exit 6

            fi

        elif [ "$1" == 'restart' ]; then

            if [ -f $lockFile];then

                rm -f $lockFile

                [ $? -eq 0 ]&& echo "Stopping $srv OK."

            else

                echo "$srvis stopped yet."

            fi

            touch $lockFile

            [ $? -eq 0 ]&& echo "Starting $srv OK."

        elif [ "$1" == 'status' ];then

            if [ -f $lockFile];then

                echo "$srvis running."

            else

                echo "$srvis stopped."

            fi

        else

            echo "Usage:$srv {start|stop|restart|status}" && exit 9

        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 "canlogin"

     else

         echo "cannotlogin"

     fi

 

5.6 bash条件测试之case语句

    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 "adigit"

         ;;

     [a-z])

         echo "achar"

         ;;

     *)

         echo "a specialword"

         ;;

     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 "$srvis 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 "wrongoption."

         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