zoukankan      html  css  js  c++  java
  • bash/shell编程学习(2)

    先来复习上节重定向的用法:

    1.快速清空文件

    cat demo.txt < /dev/null
    

    注:linux中有一个经典名言【一切皆文件】,/dev/null可以认为是一个特殊的空文件,更形象点,可以理解为科幻片中的黑洞,任何信息重向定输出到它后,便有去无回,当然黑洞里也没有信息能出来。

    综合来讲,上面的意思就是利用<将黑洞做为demo.txt的标准输入,黑洞里没任何内容,任何文件里的内容被它吞噬了,自然也没就没东西能剩下了,所以最终就是demo.txt被黑洞洗空了。

    /dev/null 还有其它用法,比如用它可以让nohup不生成nohup.out文件,见:http://www.cnblogs.com/yjmyzz/p/4831182.html 

    2.执行时输出源码

    #!/bin/bash -v
    printf '%0.2f
    ' 12.12334
    

    执行结果如下:

    #!/bin/bash -v
    printf '%0.2f
    ' 12.12334
    12.12
    

    注意:第3行输出结果之前,把源码也打印出来了,秘密在于第1行最后的 -v 参数 

    3.调试模式

    #!/bin/bash -x
    printf '%0.2f
    ' 12.12334
    echo 'hello'
    

    执行结果如下:

    + printf '%0.2f
    ' 12.12334
    12.12
    + echo hello
    hello
    

    注意:第一行后面的参数变成了-x,加上这个后,执行时,每一行代码在执行前,会先输出对应的源码,并且以+开头,十分方便调试。  

    4. if与test及[]

    4.1 数字判断

    #!/bin/bash -x
    i=$1 #变量i的值取第1个参数的值
    if test $i -gt 89; then #如果i>89
      echo 'A'
    elif test $i -gt 79; then #如果i>79
      echo 'B'
    elif test $i -eq 60 -o $i -gt 60;then #如果i=60或i>60(即:i>=60)
      echo 'C'
    elif test $i -gt 0;then #如果i>0
      echo 'D'
    elif test $i -lt 0;then #如果i<0
      echo 'invalid'
    else #i==0的情况
      echo 'zero' 
    fi
    

    注:if test 条件; then 语句 fi 这是基本格式,注意条件后的;不可省略,另外结束符号是fi(即:把if倒过来,有点回文的理念),另外要记住一堆缩写

    -lt 即-Less Than的缩写,表示小于

    -gt 即-Greater Than的缩写,表示大于

    -eq 即-equal的缩写,表示等于,此外还有

    -ne 即-Not Equal的缩写,表示不等于

    -o 即-or,表示前后二个逻辑判断是『』的关系,类似的

    -a 即-and,表示前后二个逻辑判断是『』的关系

    elif 即else if的缩写 

    上面的示例运行结果:

    ./demo.sh 90
    + i=90
    + test 90 -gt 89
    + echo A
    A
    

    test语句还有一个简化的写法,即把"test 条件"变成" [ 条件 ] ",注意二端的方括号左右都要加一个空格,所以上面的写法可以改成:

    i=$1
    if [ $i -gt 89 ]; then
      echo 'A'
    elif [ $i -gt 79 ]; then
      echo 'B'
    elif [ $i -eq 60 -o $i -gt 60 ]; then
      echo 'C'
    elif [ $i -gt 0 ]; then
      echo 'D'
    elif [ $i -lt 0 ]; then
      echo 'invalid'
    else
      echo 'zero'
    fi
    

    这样看起来就美观多了,如果不喜欢-o这种逻辑或的写法,第6行也可以换成这样

    elif [ $i -eq 60 ] || [ $i -gt 60 ]; then
    

    但是执行的细节略有区别,在调试模式下可以对比下,用||写法的输入(测试用例:61)

    ./demo2.sh 61
    + i=61
    + '[' 61 -gt 89 ']'
    + '[' 61 -gt 79 ']'
    + '[' 61 -eq 60 ']'
    + '[' 61 -gt 60 ']'
    + echo C
    C
    

    而用-o写法的输出:

    ./demo2.sh 61
    + i=61
    + '[' 61 -gt 89 ']'
    + '[' 61 -gt 79 ']'
    + '[' 61 -eq 60 -o 61 -gt 60 ']'
    + echo C
    C
    

    对比下5-6行可以发现,区别在于判断一次,还是判断二次 

    4.2 字符串判断

    #!/bin/bash -x
    str1="abc"
    if [ -z "$str1" ]; then
      echo 'str1 is empty'
    else
      echo 'str1 is not empty'
    fi
    
    printf "
    "
    
    str2=""
    if [ -n "$str2" ]; then
      echo 'str2 is not empty'
    else
      echo 'str2 is empty'
    fi
    
    printf "
    "
    
    if [ "$str1" = "$str2" ]; then
      echo 'str1 = str2'
    else
      echo 'str1 <> str2'
    fi
    

    注: -n即-not empty判断字符串非空,-z即-zero判断字符串为空,=判断字符串相同(判断字符串时,记得要加双引号)

    运行结果:

    + str1=abc
    + '[' -z abc ']'
    + echo 'str1 is not empty'
    str1 is not empty
    + printf '
    '
    
    + str2=
    + '[' -n '' ']'
    + echo 'str2 is empty'
    str2 is empty
    + printf '
    '
    
    + '[' abc = '' ']'
    + echo 'str1 <> str2'
    str1 <> str2
    

    4.3 文件及目录判断

    #!/bin/bash -x
    if [ -f ~/.bash_profile ]; then
      echo '~/.bash_profile is a file'
    else
      echo '~/.bash_profile is not a file'
    fi
    
    printf '
    '
    
    if [ -d ~/ ]; then
      echo '~/ is a directory'
    else
      echo '~/ is not a directory'
    fi
    

    -f即判断是否为file, -d即判断是否为directory, 输出结果:

    + '[' -f /Users/yjmyzz/.bash_profile ']'
    + echo '~/.bash_profile is a file'
    ~/.bash_profile is a file
    + printf '
    '
    
    + '[' -d /Users/yjmyzz/ ']'
    + echo '~/ is a directory'
    ~/ is a directory
    

    5.命令列表

    命令1 && 命令2

    解释:如果命令1返回成功,则命令2会执行,示例:

    #!/bin/bash
    ping -c 4 $1 && printf '
    ==== %s connected ====
    ' $1
    

    将上面这段保存成testurl.sh,然后chmod +x testurl.sh,执行效果如下:

    ./testurl.sh www.baidu.com
    PING www.a.shifen.com (115.239.211.112): 56 data bytes
    64 bytes from 115.239.211.112: icmp_seq=0 ttl=50 time=9.950 ms
    64 bytes from 115.239.211.112: icmp_seq=1 ttl=50 time=23.994 ms
    64 bytes from 115.239.211.112: icmp_seq=2 ttl=50 time=12.272 ms
    64 bytes from 115.239.211.112: icmp_seq=3 ttl=50 time=19.717 ms
    
    --- www.a.shifen.com ping statistics ---
    4 packets transmitted, 4 packets received, 0.0% packet loss
    round-trip min/avg/max/stddev = 9.950/16.483/23.994/5.641 ms
    
    ==== www.baidu.com connected ====
    

    如果把后面的参数 ,换成某个不能访问的网址,比如在伟大的墙内,可以试下:

    ./testurl.sh www.google.com
    PING www.google.com (216.58.197.100): 56 data bytes
    Request timeout for icmp_seq 0
    Request timeout for icmp_seq 1
    Request timeout for icmp_seq 2
    
    --- www.google.com ping statistics ---
    4 packets transmitted, 0 packets received, 100.0% packet loss
    

    命令1 || 命令2

    解释:这个正好跟&&相反,如果命令1返回失败,则执行命令2

    #!/bin/bash
    ping -c 4 $1 || printf '
    ==== %s connect fail ====
    ' $1
    

    把这个保存成testurl2.sh ,然后重复刚才的测试

    ./testurl2.sh www.google.com
    PING www.google.com (216.58.199.4): 56 data bytes
    Request timeout for icmp_seq 0
    Request timeout for icmp_seq 1
    Request timeout for icmp_seq 2
    
    --- www.google.com ping statistics ---
    4 packets transmitted, 0 packets received, 100.0% packet loss
    
    ==== www.google.com connect fail ====
    

    通过刚才的测试,相信大家已经掌握&&与||的用法了,那么问题来了,如何判断前一个命令的执行结果是【成功】还是【失败】呢

    先回忆一下,大学里《C程序设计》里老师讲的内容,C程序里main函数,如果运行成功,最后一般会约定返回return 0,没错bash里就是这么判断的

    (再提一个问题:为什么要跟C扯上关系?因为linux里的很多bash命令,就是拿C/C++来开发的),我们可以来验证下:

    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc,char **argv){
        printf("hello world and this function will return 0
    ");
        return 0;
    }
    

    这是一段c语言的代码,保存成hello1.c,然后输入gcc -o hello1 hello1.c (mac本上只要安装了xcode,就已经自带了gcc编译器),然后会在当前目录下生成hello1的可执行文件,做为对比,再来一个hello2.c

    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc,char **argv){
        printf("hello world and this function will return 1
    ");
        return 1;
    }
    

    同样编译成hello2,然后测试:

    ./hello1 && echo 'hello1 is ok'
    hello world and this function will return 0
    hello1 is ok
    

    再来一个

    ./hello2 && echo 'you can not see this'
    hello world and this function will return 1
    

    小结:这跟很多语言里约定1代表true, 0代表false正好是反的,在bash里,如果一个命令执行后返回0,表示成功,返回1表示失败。  

    6. 检测参数个数及类型

    最后结合前面学到的知识,做一个小小的综合练习:

    #!/bin/bash
    
    echo 'param count: ' $#
    echo 'first param: ' $1
    if [ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null); then
       echo 'param check pass!'
    else
       echo 'only one integer parameter is accepted!'
    fi
    

    上面这段代码的意思是仅接收1个整型的参数,将这段代码保存成check1.sh,然后试着运行下:

    ./check1.sh a b 2
    param count:  3
    first param:  a
    only one integer parameter is accepted!
    

    再试下:

    ./check1.sh 123
    param count:  1
    first param:  123
    param check pass!
    

    第5行的那个长长的if判断,初次看估计比较晕,我们来分解一下:

    第一部分

    [ $# -eq 1]
    

    其中$#表示参数的个数,-eq 1 要求参数个数必须等于1

    第二部分

    (echo $1 | grep ^[0-9]*$ >/dev/null) 
    

    仍然有点复杂,再细分一下,先不管最后的>/dev/null,将其去掉,然后简化一下:

    grep 用于字符查找及过滤,见下面的图:

      

    who用于显示本机有哪些用户登录了,以及登录的终端信息,加上管道符|,将输出结果传递给grep 001 ,最后就从who的一堆结果中,过滤出包含001的信息了。

    再回过头,看下这个:

    echo 123 | grep ^[0-9]*$
    

    会输出123(注:如果mac上将终端改成了zsh,直接运行会报错zsh: no matches found: ^[0-9]*$,解决办法:新建一个.sh脚本文件,写在脚本文件里就能运行了),grep后的部分是一个正则表达式,匹配0-9中的1个或多个,最后再来看:

    (echo $1 | grep ^[0-9]*$ >/dev/null)
    

    现在应该能看懂了吧,将1个参数输出,然后做为grep的输入,正常情况下,如果第1个参数为数字,则会输出,但是我们的本意是放在if条件判断中,并不希望将其输出,所以最后重定向到黑洞。

    结合前面的命令列表&&,可以将这段if简化成终极版本:

    #!/bin/bash -x
    
    ! ([ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null)) && echo 'only one integer parameter is accecpted ' &&  exit 1
    echo 'param check pass!'
    

    就不解释了,大家自己体会吧。  

      

  • 相关阅读:
    Delphi 7下使用VT实现树型列表结合控件
    Spring:源码解读Spring IOC原理
    【HTTP】Fiddler(二)
    简单工厂模式、工厂方法模式、抽象工厂模式 之间的对比
    UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)
    Tomcat 的context.xml
    Tomcat的context.xml说明、Context标签讲解
    Node.js
    区块链架构设计
    什么是区块链
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/bash-shell-programming-study-2.html
Copyright © 2011-2022 走看看