bash是linux学习中重要的一环,他可以批量处理linux命令,完成多个单任务的组合。提高运维效率!

bash编程语法一:变量

        bash变量类别:

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

        VAR_NAME=VALUE

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

        变量引用:${VAR_NAME}

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

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

[root@www ~]# varname=aolens.com
[root@www ~]# echo ${aolens.com}
-bash: ${aolens.com}: bad substitution
[root@www ~]# echo ${varname}
aolens.com
[root@www ~]# echo "$varname"
aolens.com
[root@www ~]# echo '$varname'
$varname

环境变量:对当前shell进程及其子shell有效,对其它的shell进程无效;

        定义:export VAR_NAME=VALUE

        导出:export VAR_NAME 

        使用env命令显示所有的环境变量

        撤消变量:unset VAR_NAME

        只读变量:readonly VAR_NAME

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

        local VAR_NAME=VALUE

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

        $1(第一个参数), $2(第二个参数), ..., ${10}

        $0(代表变量本身)

特殊变量:

$#位置参数的个数
"$*"代表"$1 $2 $3 $4..."空格间隔
"$@"所有的位置参数(每个都独立“$1” "$2" "$3" ...)
${#*}传递到脚本中的命令行参数的个数
${#@}传递到脚本中的命令行参数的个数
$?返回值
$$脚本的进程ID(PID)
$!运行在后台的最后一个作业的进程ID(PID)

set可以设置某个环境变量的值。清除环境变量的值用unset命令。如果未指定值,则该变量值将被设为NULL。

变量命名:

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

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

        3、要见名知义

变量类型:

        精确数值:整数

        近似数值:浮点型

        字符型:char,string

        布尔型:true, false


bash的配置文件:

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

全局:/etc/profile、/etc/profile.d/*.sh

用户:~/.bash_profile

bashrc类:为非交互式的用户提供配置

全局:/etc/bashrc

用户:~/.bashrc


bash编程:解释器

    shell脚本:

        第一行,顶格:

    #!/bin/bash-->用来识别是否bash脚本
    #!/usr/bin/python

        其它的以#开头的行均为注释,会被解释器忽略;可作为脚本内命令说明。

bash编程:测试脚本 

        -n: 语法测试,测试是否存在语法错误

        -x: 模拟单步执行,将每一步的执行过程结果都显示出来。

bash编程:for循环语句 

        for第一种格式:

        for 变量名 in 列表; do

            循环体

        done

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

        循环可以嵌套;

        退出条件:遍历列表完成

    for i in {1..10};do
        echo $i
    done

        for的第二种使用格式 : 

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

            循环体

        done

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

    for((i=0;i<10;i++));do    
        echo $i
    done

bash编程:while、until循环语句              

        while格式:

        while 测试条件; do

            循环体

        done

    read -p "enter num" num    
    while [ $num -gt 10 ];do
        echo  ‘num>10’
    read -p "enter num" num
    done

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

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

        until格式:

        until 测试条件; do 

            循环体

        done

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

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

bash编程:交互式编程 

        read [option] “prompt”

        -p:直接指定一个变量接受参数

        -t timaout:指定等待接受参数的时间

        -n:表示不换行   

    例如:输入用户名,可返回其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

bash编程:逻辑运算

        &&(与): condition1 && condition2 == [ condition1 -a condition2 ]

        ||(或): condition1 || condition2 == [ condition1 -o  condition2 ]

       !(非):! condition   == [ -not  condition ]

bash编程:if选择

        if: 三种使用格式

        (1)单分支的if语句:表示条件测试状态返回值为值,则执行选择分支;

        if 测试条件; then

           选择分支

        fi

         (2)双分支的if语句:两个分支仅执行其中之一

        if 测试条件; then

           选择分支1

        else

            选择分支2

        fi

   练习:通过命令行给定一个文件路径,而后判断:
   如果此文件中存在空白行,则显示其空白行的总数;
   否则,则显示无空白行;
   if grep "^[[:space]]*$" $1 &> /dev/null; then
   echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
   else
   echo "No blank lines"
   fi

注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。

        (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编程:case选择

        case语句:有多个测试条件时,case语句会使得语法结构更明晰

        case格式:

        case  变量引用  in

        PATTERN1)

        分支1

        ;;

        PATTERN2)

        分支2

        ;;

        ...

        *)

       分支n

        ;;

       esac

       PATTERN:类同于文件名通配机制,但支持使用|表示或者;

       a|b: a或者b

       *:匹配任意长度的任意字符

       ?: 匹配任意单个字符

       []: 指定范围内的任意单个字符    

    写一个脚本,使用格式:    
    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
    if [ "$1" == 'start' ]; then
       if [ -f $lockFile ]; then
            echo "$srv is 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 "$srv is stopped yes."
            exit 6
        fi
    elif [ "$1" == 'restart' ]; then
        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."
    elif [ "$1" == 'status' ];then
        if [ -f $lockFile ];then
            echo "$srv is running."
        else
            echo "$srv is stopped."
        fi
    else
        echo "Usage: $srv {start|stop|restart|status}" && exit 9
    fi




bash编程:算术运算

        bash会对数字执行隐式的类型转换

        declare -i Var_Name=Integer_Value:定义一个整型变量

        let Var_Name=EXPRESSION

        $[EXPRESSION]

        $((EXPRESSION))

        命令:expr ARG1 OP ARG2

        操作符:

        +, -, *, /, %(取模), **(次幂)

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

bash编程:条件测试

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

        测试类型:根据比较时的操作数的类型

        整型测试:整数比较

        字符测试:字符串比较

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

        (1)整型测试:

        例如 [ $num1 -gt $num2 ]

-gt大于
-lt小于
-ge大于等于
-le小于等于
-eq等于
-ne不等于
-n string(单目测试)
是否不空,不空则为真,空则为假
-z string (单目测试)
是否为空,空则为真,不空则假

        (2)字符串测试:

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

>大于
<小于
>=大于等于
<=小于等于
==等于
!=不等于

        (3)文件测试:

-a file存在则为真;否则则为假
-e file存在则为真;否则则为假
-f file存在并且为普通文件,则为真;否则为假
-d file存在并且为目录文件,则为真;否则为假
-L/-h file存在并且为符号链接文件,则为真;否则为假
-b
存在并且为块设备,则为真;否则为假
-c
存在并且为字符设备,则为真;否则为假
-S
存在并且为套接字文件,则为真;否则为假
-s
存在并且飞空,则为真;否则为假
-p
存在并且为命名管道,则为真;否则为假
-r
文件可读为真,否则为假
-w
文件可写为真,否则为假
-x
文件可执行为真,否则为假


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

bash编程:信号捕捉

        trap 'COMMAND' SIGNALE

        第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双
        引号中的命令串。
        trap 'commands' SININT(表示关闭进程)
        trap "commands" SIGINT(表示关闭进程)       

    脚本:写一个脚本,能够ping探测指定网络内的所有主机是否在线?当没有执行完时可接收ctrl+c命令退出!    
    #!/bin/bash
    #
    quitScript() {
        echo "Quit..."
    }    
    trap 'quitScript; exit 5' SIGINT
    cnetPing() {
        for i in {1..254}; do
    if ping -c 1 -W 1 $1.$i &> /dev/null; then
         echo "$1.$i is up."
    else
         echo "$1.$i is down."
    fi
         done
    }
    bnetPing() {
        for j in {0..255}; do
    cnetPing $1.$j 
        done
    }
    anetPing() {
        for m in {0..255}; do
    bnetPing $1.$m
        done
    }
    netType=`echo $1 | cut -d"." -f1`
    if [ $netType -ge 1 -a $netType -le 126 ]; then
        anetPing $netType
    elif [ $netType -ge 128 -a $netType -le 191 ]; then
        bnetPing $(echo $1 | cut -d'.' -f1,2)
    elif [ $netType -ge 192 -a $netType -le 223 ]; then
        cnetPing $(echo $1 | cut -d'.' -f1-3)
    else
        echo "Wrong"
        exit 2
    fi

bash编程:shift

       如果没有数字,只有shift 就是跳过一个参数获取下一个参数,如果加上数字,比如shift 2 ,跳过两个参数获取下一个参数
  比如:
  $1=1 $2=2 $3=3
  那么shift后
  $1=2 $2=3 $3=""
  如果shift 2后
  $1=3 $2="" $3=""

    练习:写一个脚本,使用形式如下所示    
    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编程:循环控制 return,break

        continue: 提前进入下一轮循环

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

        break [n]:跳出当前循环

        用于条件语句中

    例如:查看用户登录    
    #!/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."
    #!/bin/bash
    #
    read -p "Plz enter a username: " userName
    until who | grep "\<$userName\>" &> /dev/null; do
      sleep 5
      echo "not here"
    done
    echo "here"