1. 使用多个命令(;):

    如果需要两个或多个命令一起执行,用分号把这些命令隔开;

    #date ; ifconfig eth0

    Sat Nov  1 08:47:46 CST 2014

    eth0      Link encap:Ethernet  HWaddr 00:50:56:9F:22:36  

              inet addr:192.168.57.23  Bcast:192.168.57.255  Mask:255.255.255.0

              inet6 addr: fe80::250:56ff:fe9f:2236/64 Scope:Link

              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

              RX packets:1785133 errors:13 dropped:13 overruns:0 frame:0

              TX packets:1707231 errors:0 dropped:0 overruns:0 carrier:0

              collisions:0 txqueuelen:1000 

              RX bytes:245612681 (234.2 MiB)  TX bytes:1915997555 (1.7 GiB)

              Interrupt:18 Base address:0x2000 


2. 显示文本(echo):

    echo的命令格式:

        echo [option] [STRING]

        

    option:

        -n: 不打印换行符

        -e: 启用转义符

        -E:禁用转义符(缺省设置)


    -e选项可以使用的转义字符:

        \\: 反斜杠

        \a: 响铃

        \b: 退格键

        \n: 换行符

        \t: 水平制表符

        \v: 垂直制表符

        \0NNN:

            \033[##n:

                第一个#:3表示前景色

                         4表示背景色

                第二个#:颜色,1-7

             \033[0m:控制符结束

    #echo -e "\033[31mHello World\033[0m"

    Hello World


3. 变量

    

变量的类型:

    本地变量: 只对当前shell有效,对其子shell以及其它shell都无效;

    定义变量:[set] Var_Name="Value"

    引用变量: $Var_Name

    撤消变量: unset Var_Name

    局部变量:仅对局部代码有效

        local Var_Name="Value"

    环境变量:

    全局环境变量:对当前shell及其子shell有效;

        printenv: 查看全局环境变量

        export Var_Name="Value" 设置全局环境变量

    局部环境变量:只对创建它们的shell有效;

    set: 查看局部环境变量;

    Var_Name="Value" 设置局部环境变量

    

    

    

    PS1: 控制默认命令提示符的格式

    PS2: 控制后续命令行提示符的格式

    echo $PS1

    [\u@\h \W]\$

    echo $PS2

    >

    在提示符中使用的特殊字符总结:

    \a: 报警字符

    \d: "日月年"格式显示的日期

    \e: ASCII转义字符

    \h: 本地主机名

    \H: 完全限定域名(FQDN)

    \j: shell当前管理的任务数

    \l: shell终端设备中的基名

    \n: ASCII换行符

    \r: ASCII回车符

    \t: 24小时制HH:MM:SS格式的当前时间 

    \T: 12小时制的当前时间

    \@: 12小时制am/pm格式的当前时间

    \u: 当前用户的用户名

    \v: bash shell的版本

    \V: bash shell的发行版本

    \w: 当前工作目录

    \W: 当前工作目录的基名

    \!: 这个命令在bash shell历史记录中的位置

    \#: 这个命令在当前命令行的位置

    \$: 普通用户下的$,root用户下的#

    \nnn: 与八进制数nnn对应的字符

    \\: 反斜线\

    \[: 开始一个控制字符序列

    \]: 结束一个控制字符序列

    位置变量:

    $1 $2 ... $n

    ./first.sh 1 2

    $1:1

    $2:2


    特殊变量:

        $0: 脚本名称自身

        $?: 上一条命令的执行状态;

            状态用数字来表示: 0 - 255

            0: 表示命令执行成功

            1-255:表示命令执行失败

        $#: 位置参数的个数

        $@:引用所有的位置参数

        $*:引用所有的位置参数

4. 反引号(``):用于引用命令命令执行的结果

    #date_now=`date`

    #echo $date_now

    Sat Nov 1 09:27:04 CST 2014


5. 输入输出重定向


标准输入:stdin-->0

标准输出:stdout-->1

标准错误: stderr-->2


输入重定向:

    <: 输入重定向:

    << EOF:用于在脚本中创建文件或生成菜单

输出重定向:

    >: 覆盖输出

    >>: 追加输出

    set -C: 禁止使用覆盖重定向至已经存在的文件;

    set +C: 关闭上术特性

    >|: 在-C特性下,强制使用覆盖重定向


同时重定向标准输出和错误输出:

    COMMAND > /path/to/outfile 2> /path/to/errfile

    COMMAND &> /path/to/somefile

    COMMAND > /path/to/somefile 2> &1

    在脚本中重定向:

    临时重定向

#!/bin/bash 

echo "this is an error" >&2

echo "this is an output."

永久重定向

#!/bin/bash 

exec 1>testout

echo "This is test of redrecting all output."

echo "from a script to another file."

把所有的标准输出重定向到文件testout;

#!/bin/bash 

exec 2>testerror

yy

重定向所有的标准错误重定向到文件testerror


在脚本中重定向输入

exec 0<testfile

从文件testfile中获取输入,而不是从0(标准输入);

#!/bin/bash

exec 0<testfile

count=1

while read line; do

echo "Line#$count:$line"

let count+=1

done

注:read命令用于读取用户在键盘上的标准输入数据,将stdin重定向到文件后,read会到文件去读取数据,而不是stdin.


创建自定义的重定向

除了0 1 2三个默认的文件描述符(FD)之外,还有3-8几个可以使用的文件描述符

创建输出文件描述符

#!/bin/bash 

exec 3>test13out

echo "this should display on the monitor"

echo "this should be stored in the file" >&3

echo "this should display on the monitor"

注:exec将fd 3重定向到文件test13out,所以重定向到&3的文件将被写入test13out


6. 管道(COMMAND1 | COMMAND2):前一个命令的输出作为后一个命令的输入

    #ifconfig eth0 | grep "inet addr"

    inet addr:192.168.57.23  Bcast:192.168.57.255  Mask:255.255.255.0



7. 执行算术运算

    执行算术运算的四种方式:

        let varName=算术表达式

        varName=$[算术表达式]

        varName=$((算术表达式))

        varName=`expr $num1 + num2`

    编写脚本:test.sh

        #!/bin/bash

        let varName1=1+1

        varName2=$[1+2]

        varName3=$((1+3))

        varName4=`expr 1 + 4`

        echo "varName1:$varName1"

        echo "varName2:$varName2"     

        echo "varName3:$varName3" 

        echo "varName4:$varName4" 

        #sh test.sh       

        varName1:2

        varName2:3

        varName3:4

        varName4:5

    

8. 脚本退出状态码

    变量$?用于保存最后一条命令退出的状态码,默认情况下,一个成功结束的命令的退出码为0,也可以在脚本中自定义退出码:

    exit num(num为保留值之外的数字)

        num: 0 - 255

        0: 命令成功结束

    1:通用未知错误 

    2:误用shell命令

    126:命令不可执行

    127: 没找到命令

    128: 无效退出参数

    128+x: Linux信号x的严重错误

    130: 命令通过Ctrl + C终止

    255: 退出状态码越界

        

9. 脚本的逻辑控制

      if 语句:

        单分支:

        if 条件;then

            分支1;

        fi

        双分支:

        if 条件;then

            分支1;

        else 

            分支2;

        fi

        多分支:

        if 条件;then

            分支1;

        elif 条件2;then

            分支2;

        elif 条件3;then

            分支3;

            ...

        else

            分支n; 

        fi

     

        例:

        if [ ! -d /ccdb ]; then

            mkdir /ccdb

        fi


     for 循环语句:

        for varName in 列表;do

            循环体

        done

    关于列表读取:

        1.直接从列表中读取,比如:for varName in /etc/sysctl.conf /etc/passwd /etc/group

        2.从变量中读取,比如:for varName in $varName

        3.从命令中读取,比如:for varName in `cat /etc/passwd`

        4.从通配符读取目录,比如:for varName in /etc/init.d/*

    C语言风格的for格式:

        for (( variable assignment; condition; iteration process ))

        比如:

            for (( i=1; i<=10; i++))

                do

                echo "The next number is $i"

            done

        例1:

        for fileName in /etc/{passwd,shadow,group}; do

            echo $fileName

        done


        例2:

        for fileName in /etc/*; do

            echo $fileName 

        done


        例3:

        for fileName in `cat /etc/passwd`; do

            echo $fileName

        done


        例4:

        varName=`cat /etc/passwd`

        for fileName in $varName; do

            echo $fileName

        done


        例5:

        for i in {1..10}; do 

            echo $i

        done


    while 循环语句:

        while 条件测试;do

        循环体

        done

当条件测试为真(测试命令返回的是退出状态码0)时,一直循环,直到退出状态码为非0


        例:        

        #!/bin/bash

        i=0

        while [ $i -le 10 ]; do  #i小于10时,执行循环;当i大于10时,循环结束。

            let i++

            echo $i

        done


    until 循环语句:

until 条件测试;do 

循环体;do 

done

当条件测试退出状态码为非0时执行循环,一旦测试命令退出状态码为0,循环结束


        例:

        #!/bin/bash

        i=0

        while [ $i -ge 10 ]; do  #当i小于10时,执行循环;当i大于10时,循环结束。

            let i++

            echo $i

        done


    case语句的语法格式:

        case用于替代if-then-else的简便形式

        case expression in

        pattern1)

        suite1 

        ;;

        pattern2)

        suite2

        ;;

        ...

        patternn)

        suiten

        ;;

        *)

        other_suite

        ;;

        esac

        

        例:

        

        #!/bin/bash

        #

        myService=`basename $0`

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


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


        case $1 in

        'start')

                touch $lockFile

                echo "Starting $myService OK"

                ;;

        'stop')

                rm -f $lockFile

                echo "Stopping $myService OK"

                ;;

        'restart')

                rm -f $lockFile

                touch $lockFile

                echo "Restarting $myService OK"

                ;;

        'status')

                if [ -f $lockFile ]; then

                        echo "$myService is running"

                else

                        echo "$myService is stopped"

                fi

                ;;

        *)

                echo "Usage: $myService {start|stop|restart|status}"

                exit 3

                ;;

        esac


10. 条件测试    

    条件判断的常用类型:

整数测试

字符测试

文件测试


    逻辑运算:

与运算:表达式1 && 表达式2

或运算:表达式1 || 表达式2

非运算:! 表达式

    bash中如何做测试:

test EXPRESSION

[ EXPRESSION ]

` EXPRESSION `


    整数测试:

二元测试:

num1 OPRAND num2

-gt: 大于

-lt: 小于

-ge: 大于等于

-le: 小于等于

-ne: 不等于

-eq: 等于

        例:

        #/bin/bash

        sum=0

        i=1

        while [ $i -le 100 ]; do

            let sum+=$i

            let i++

        done

        echo $sum

            

        

    字符测试:

单目:

-n $stringVar: 字符串是否为空,不空为真,空为假;

-z $stringVar: 字符串是否为空,空为真,不空为假;

        例:

        #/bin/bash

        s="hello"

        if [ -n $s]; then

            echo "false"

        fi


双目:

!=: 不等于

>: 大于

<: 小于

==: 等于,等值比较

=~左侧是字符串,右侧是模式,判定左侧的字符串能否被右侧的模式所匹配,通常在[[]]中使用;

        例:

        

        #!/bin/bash

        #

        s="hello world"

        [[ $s =~ "hello" ]] && echo "success"


    文件测试:

单目:

-b file: 测试file是否存在且是否是block设备

-c file: 测试file是否存在且是否是字符设备

-d file: 测试file是否存在且是目录

-e file: 测试file是否存在

-f file: 测试文件是否存在且是一个普通文件

-g file: FILE exists and is set-group-ID

-G file: FILE exists and is owned by the effective group ID

-h file: 测试file是否存在且是一个链接文件和-L功能一样

-L file: 

-O file: FILE exists and is owned by the effective user ID

-p file: 测试file是否存在且是一个管道文件

-r file: 测试file是否存在且有读权限

-s file: 测试file是否存在,文件size大于0

-S file: 测试file是否存在且是一个sock文件

-t file: 测试文件文件描述符是否被一个终端打开

-u file:  FILE exists and its set-user-ID bit is set

-w file: 测试file是否存在且有写权限

-x file: 测试文件是否存在且有执行权限

        

        例:

        #!/bin/bash

        if [ ! -d /backup ]; then

            mkdir /backup

        fi

双目:

FILE1 -ef FILE2:FILE1 and FILE2 have the same device and inode numbers

        FILE1 -nt FILE2:FILE1 is newer (modification date) than FILE2

        FILE1 -ot FILE2:FILE1 is older than FILE2     

        

        例:

        #!/bin/bash

        touch /tmp/a.txt

        ln -s /tmp/a.txt /tmp/b.txt

        fileName1=/tmp/a.txt

        fileName2=/tmp/b.txt

        [ $fileName1 -ef $fileName2 ] && echo "they are the same file."


    bash编程之组合测试条件深入探讨:


    逻辑与:多个条件同时满足

[ CONDITION1 ]  &&  [ CONDITION2 ] 

[ CONDITION1 -a CONDITION2 ]

[[ CONDITION1 && CONDITION2 ]] 


注意:前两个使用单或双中括号都可,但,&&不允许用于单中括号中,所以第三种只能用于双中括号中;


    逻辑或:多个条件中有一个满足即为真;

[ CONDITION1 ]  ||  [ CONDITION2 ] 

[ CONDITION1 -o CONDITION2 ]

[[ CONDITION1 || CONDITION2 ]] 

注意:||不允许用于单中括号中;


      例:脚本1

        #!/bin/bash

        if [ -d /backup ] && [ -f /backup/test.txt ]; then

                echo "This file exist!"

        fi

      例:脚本2

        #!/bin/bash

        if [[ -d /backup && -f /backup/test.txt ]]; then

                echo "This file exist!"

        fi

        脚本1和脚本2的效果完全一样。


    if-then的高级特性

        使用双尖括号(( expression ))

            术语expression可以是任意的数学赋值或比较表达式

            双尖括号命令符号:

                var++:    后增

                var--:    后减

                ++var:    先增

                --var:    先减

                !:        逻辑求反

                ~:        位求反

                **:       求幂

                <<:       左位移

                >>:       右位移

                &:        位布尔和

                |:       位布尔或

                &&:       逻辑和

                ||:      逻辑或

        例:

        #!/bin/bash

        val1=10

        if (( $val1 ** 2 > 90 )); then

            (( val2 = $val1 ** 2 ))

            echo "The square of $val1 is $val2"

        fi

                            

        使用双方括号` expression `

            双方括号里的expression使用了test命令中采用的标准字符串进行比较。但它提供了test命令未提供的另一个特性(模式匹配)

        例:

        #!/bin/bash

        if [[ $USER == r* ]]; then

            echo "Hello $USER"

        else

            echo "Sorry,I do not know you!"

        fi


11. 处理用户输入

    位置参数:$1 $2 ... $n

        例:test2.sh

        #!/bin/bash

        [ $# -ne 2 ] && echo "Usage: `basename $0` num1 num2"

        let total=$1 * $2

        echo "The first parameter is $1."

        echo "The second paramter is $2."

        echo "The total value is $total."

        echo "This script name is `basename $0`


        #sh test3.sh 2 5

        The first parameter is 2.

        The second paramter is 5.

        The total value is 10.

        This script name is test3.sh.

     

    $* 和 $@:用于提取所有位置参数

        例: 脚本test4.sh       

        #!/bin/bash

        count=1

        for param in "$*"; do

                echo "\$* parameter #$count = $param"

                count=$[$count+1]

        done


        count=1

        for param in "$@"; do

                echo "\$@ parameter #$count = $param"

                count=$[$count+1]

        done

        #sh test4.sh apple banana juice pea

        $* parameter #1 = apple banana juice pea

        $@ parameter #1 = apple

        $@ parameter #2 = banana

        $@ parameter #3 = juice

        $@ parameter #4 = pea

    

    变量移动:

        shift:

            默认情况下,shift会将每个参数变量减1;变量$3的值会移到$2,变量$2的值会移到$1,而变量$1的值会被删除。

        例1:

        #!/bin/bash

        sum=0

        for i in `seq 1 $#`; do

                let sum+=$1

                shift

        done

        echo $sum

        

        例2:  

        #!/bin/bash

        #

        [ $# -lt 2 ] && echo "Too less argements, quit" && exit 3


        if [[ "$1" == "-u" ]];then

          userName="$2"

          shift 2

        fi


        if [ $# -ge 2 ] && [ "$1" == "-v" ]; then

          verFlag=$2

        fi


        verFlag=${verFlag:-0}


        if [ -n $verFlag ];then

          if ! [[ $verFlag =~ [012] ]];then

            echo "Wrong parameter."

            echo "Usage: `basename $0` -u UserName -v {1|2}"

            exit 4

          fi  

        fi


        # echo $userName $verFlag


        if [ $verFlag -eq 1 ]; then

              grep "^$userName" /etc/passwd | cut -d: -f1,3,4,6

        elif [ $verFlag -eq 2 ];then 

              grep "^$userName" /etc/passwd | cut -d: -f1,3,4,6,7

        else

              grep "^$userName" /etc/passwd | cut -d: -f1,3,4

        fi