测试,判断,循环

条件测试:
判断某需求是否满足,需要由测试机制来实现;

如何编写测试表达式以实现所需的测试;        
(1)执行命令,并利用命令状态返回值来判断;
$?:上一个命令的返回值
0:成功
1-255:失败
    grep "^&" /etc/init.d/functions &> /dev/null
    echo $?  返回0表示有空白行,测试成功;
(2)测试表达式;
测试方法一:
    test expression
        test 2>3
        echo $?
        test 2<3
        echo $?
测试方法二:        
    [ expression ]
    ` expression `    
    注意:EXPRESSION两端必须有空白字符,否则为语法错误;
    
bash的测试类型:
    数值比较
    字符串测试
    文件测试
    逻辑测试

数值测试:整数比较
    -eq:是否等于; [ "$num1" -eq "$num2" ]
    -ne:是否不等于;
    -gt:是否大于;
    -ge:是否大于等于;
    -lt:是否小于;
    -le:是否小于等于;

字符串测试:
    -z "string":判断字符串是否为空,空为真,不空为假;(zero)
    -n "string":判断字符串是否不空,不空为真,空为假;(non-zero)
    =:是否等于; [ tom == Tom ] [ tom == "$name" ]
    !=:是否不等于;
        "string1" = "string2"  若串1等于串2为真,可使用"=="代替"=";
        "string1" != "string2"  若串1不等于串2为真,则不能用"=="代替"=";
    >:是否大于; (单中括号中需要转义)
    <:是否小于; (单中括号中需要转义)            
    =~:左侧字符串是否能够被右侧的PATTERN所匹配;
        name=tom
        [[ "$name" =~ o.* ]] 真;只匹配部分即为真;

注意:(1)字符串务必要加引号;
     (2)[]中,>和<都需要进行转义;
        如果不想使用转义符,可以使用双中括号[[]];
        [ a > b ]
        [ a < b ]
        [ "a" > "b" ]
        [ "a" < "b" ]
        [[ "a" > "b" ]] 真
        [[ "a" < "b" ]] 假    
            
文件测试:
    
存在性测试,存在为真,不存在为假;
    -a file  同-e
    -e file
    [ -e /etc/rc.d/rc.sysinit ]

存在性及类型测试:
    -b:是否存在且为块设备;是则为真;
        [ -b /dev/sda ]
    -c:是否存在且为字符设备文件;    
    -d:是否存在且为目录文件;
    -f:是否存在且为普通文件;
    -h或者-L:是否存在且为符号链接文件;
    -p:是否存在且为管道文件;
    -S:是否存在且为套接字文件;
    
文件权限测试:
    -r:是否存在且对当前用户可读;
    -w:是否存在且对当前用户可写;
    -x:是否存在且对当前用户可执行;
特殊权限测试:
    -u:是否存在且拥有suid权限;
        [ -u /usr/bin/su ]
    -g:是否存在且拥有sgid权限;
        [ -u /usr/bin/passwd ]
    -k:是否存在且拥有sticky权限;
        [ -k /tmp ]

文件是否有内容:
    -s:有内容为真,空则为假;
    
时间戳测试:
    -n:文件从上一次读操作后是否被修改过;
    
从属关系测试:
    -O:当前用户是否为文件的属主;
    -G:当前用户是否属于文件的属组;
    
双目测试,文件新旧比较测试:
    file1 -ef file2:file1与file2是否指向同一个文件系统的相同inode的硬链接;
    file1 -nt file2:file1是否新于file2;    (newer than)
    file1 -ot file2:file1是否旧于file2;    (older than)
    文件的新旧比较的主要使用场景是判断文件是否本更新或增量备份时;

逻辑测试,组合测试条件:
逻辑运算符:
    COMMAND1 && COMMAND2
    COMMAND1 || COMMAND2
    !COMMAND
    
逻辑运算:
    运算数:真(true,yes,on,1)
            假(false,no,off,0)
    
    与:
        1 && 1 = 1
        1 && 0 = 0
        0 && 0 = 0
        0 && 0 = 0
    或:
        1 || 1 = 1
        1 || 0 = 1
        0 || 1 = 1
        0 || 0 = 0
    非:
        !1 = 0
        !0 = 1

短路法则:
    COMMAND1 && COMMAND2
        COMMAND1为假,则COMMAND2不会再执行;
        否则,COMMAND1为真,则COMMAND2必须执行;
        
    COMMAND1 ||COMMAND2
        COMMAND1为真,则COMMAND2不会执行;
        否则,COMMAND1为假,则COMMAND2必须执行;    
    
    [ -O file ] && [ -r file ]
    
    id user3 &> /dev/null && exit 0 || useradd user3
    如果id user3为真则执行exit,如果为假,则执行useradd;    
    
逻辑测试符:
    expression1 -a expression2
    expression1 -o expression2
    !expression
    
    [ -O file -a -x file ]                    
    [ ! -e /var/log/messages ] 测试值为真的表达式在使用逻辑非后,表达式为假;    

在shell中,流程控制分为两大类:
    一类是:判断选择;(if,case)
    一类是:循环;(for,while,until,select)        

if判断结构:
    if expression;then
        COMMAND
    fi
if是最简单的判断语句,如果测试结果为真,则执行COMMAND;(单向选择)

if/else判断结构:    
    if expression;then
        COMMAND
    else
        COMMAND
    fi
如果if后的判断为真,则执行then后的命令;否则,执行else后的命令;

if/elif/else判断结构:
    if expression1;then
        COMMAND1
    elif expression2;then
        COMMAND2
    else
        COMMAND
    fi    
注意:多个条件可能同时为真,只会执行第一次测试为真的分支;

case判断结构:
    case VARIABLE in
        var1) COMMAND1 ;;
        var2) COMMAND2 ;;
        var3) COMMAND3 ;;
        *) COMMAND ;;
    esac
从上到下依次比较$VAR和var1、var2..是否相等,如果匹配则执行后面的命令,没有匹配的则默认匹配最后的*);
        

循环执行:将一段代码重复执行0、1或多次;
进入条件:条件满足时才进入循环;
退出条件:每个循环都应该有退出条件,以有机会退出循环;

bash脚本有3种循环方式:
for循环
while循环
until循环

带列表的for循环:
    for VARIABLE in LIST;do
        循环体
    done

进入条件:只要列表有可用元素即可进入循环;
退出条件:列表中的元素遍历完成;

LIST的生成方式:
    (1)直接给出;
    (2)整数列表;
        (a):{start..end}   
            echo {1..10}
            echo {a..z}
        (b):seq
            seq LAST  seq 10
            seq FIRST LAST  seq 5 10
            seq FIRST INCREMENT LAST seq 1 2 10; seq 2 2 10
            seq 100 -2 10 逐个-2到10
        (c):返回列表的命令
        (d):通配符
        (e):变量引用
            $@,$*
            
不带列表的for循环:
    for VARIABLE
    do
        COMMAND
    done

使用不带列表的for循环时,通过参数的方式给for循环传递变量值;
    vim for01.sh
    #!/bin/bash
    for VARIABLE
    do
        echo -n "$VARIABLE"
    done
    带参数执行:
    #bash for01.sh 1 2 3
    1 2 3
    
语法虽然可以工作,但是可读性较差,所以不建议使用;可以用特殊变量$@改写上述结构,功能完全一样;
    vim for02.sh
    #!/bin/bash
    for VARIABLE in $@
    do
        echo -n "$VARIABLE"
    done

类C的for循环:
    for ((expression1;expression2;expression3))
    do
        COMMAND
    done
    expression1为初始化语句,一般用作变量定义和初始化;
    expression2为判断表达式,用于测试表达式是否为真,为真则继续,为假则退出;
    expression3用于变量值修改赋值,并影响循环行为;
    vim for03.sh
    #!/bin/bash
    for ((i=1;i<=10;i++))
    do
        echo -n "$i"
    done
    #bash for03.sh
    1 2 3 4 5 6 7 8 9 10

while循环:
    while expression
    do
        COMMAND
    done
    测试expression返回值,为真则执行循环体,为假则不执行循环;

while无限循环:
    方法一
    while ((1))
    do
        COMMAND
    done
    方法二
    while true
    do
        COMMAND
    done
    方法三
    while :
    do
        COMMAND
    done

while循环的特殊用法:按行读取文件,常用于处理格式化数据;
    while read LINE
    do
        COMMAND
    done < /path/to/file
    依次读取文件中的每一行,并赋值给变量LINE;

上面采用了重定向的方式完成了文件读取,使用管道也可以完成同样的效果:
    cat /path/to/file | while read LINE
    do
        COMMAND
    done

使用重定向只会产生一个shell;
使用管道会产生3个shell:第一个shell是cat,第二个shell是管道,第三个shell是while;

until循环:
    until expression
    do
        COMMAND
    done
    测试expression返回值,为假则执行循环体,为真则不执行循环;

until无限循环:(与while相反)
    方法一
    until ((0))
    do
        COMMAND
    done
    方法二
    until false
    do
        COMMAND
    done

select循环:
    select VARIABLE in list
    do
        COMMAND
    done
是一种菜单扩展循环方式,语法和带列表的for循环相似;
执行select脚本时,将列表中的所有元素生成为可用1、2、3等书选择的菜单,并等待用户输入;
用户输入并回车后,select可判断输入并执行后续命令;
如果直接回车,select将不会退出,而是再次生成列表等待输入;

select循环不会自动退出,需要使用break退出循环,或者用exit命令终止脚本;
显示PS3提示符,默认是#?,可以自己指定提示符;直接指定PS3="xxxx"即可;
用户的输入被保存在内置变量$REPLY中;

select经常和case联合使用;

#/bin/bash
PS3="what do you want to eat? "
select VAR in gongbaojiding baoyu shanzhen quit
do
    case $REPLY in
        1) echo "you like $VAR." ;;
        2) echo "you like $VAR." ;;
        3) echo "you like $VAR." ;;
        4) echo "quit.";break ;;
        *) echo "invalid input." ;;
    esac
done