zoukankan      html  css  js  c++  java
  • Linux学习十八之、善用判断式

    原文地址:http://vbird.dic.ksu.edu.tw/linux_basic/0340bashshell-scripts_3.php


    善用判断式

    在第十一章中,我们提到过 $? 这个变量所代表的意义, 此外,也透过 && 及 || 来作为前一个命令运行回传值对於后一个命令是否要进行的依据。第十一章的讨论中,如果想要判断一个目录是否存在, 当时我们使用的是 ls 这个命令搭配数据流重导向,最后配合 $? 来决定后续的命令进行与否。 但是否有更简单的方式可以来进行『条件判断』呢?有的~那就是『 test 』这个命令。


    小标题的图示利用 test 命令的测试功能

    当我要检测系统上面某些文件或者是相关的属性时,利用 test 这个命令来工作真是好用得不得了, 举例来说,我要检查 /dmtsai 是否存在时,使用:

    [root@www ~]# test -e /dmtsai
    

    运行结果并不会显示任何信息,但最后我们可以透过 $? 或 && 及 || 来展现整个结果呢! 例如我们在将上面的例子改写成这样:

    [root@www ~]# test -e /dmtsai && echo "exist" || echo "Not exist"
    Not exist  <==结果显示不存在啊!
    

    终的结果可以告知我们是『exist』还是『Not exist』呢!那我知道 -e 是测试一个『东西』在不在, 如果还想要测试一下该档名是啥玩意儿时,还有哪些标志可以来判断的呢?呵呵!有底下这些东西喔!

    测试的标志 代表意义
    1. 关於某个档名的『文件类型』判断,如 test -e filename 表示存在否
    -e 该『档名』是否存在?(常用)
    -f 该『档名』是否存在且为文件(file)?(常用)
    -d 该『档名』是否存在且为目录(directory)?(常用)
    -b 该『档名』是否存在且为一个 block device 装置?
    -c 该『档名』是否存在且为一个 character device 装置?
    -S 该『档名』是否存在且为一个 Socket 文件?
    -p 该『档名』是否存在且为一个 FIFO (pipe) 文件?
    -L 该『档名』是否存在且为一个连结档?
    2. 关於文件的权限侦测,如 test -r filename 表示可读否 (但 root 权限常有例外)
    -r 侦测该档名是否存在且具有『可读』的权限?
    -w 侦测该档名是否存在且具有『可写』的权限?
    -x 侦测该档名是否存在且具有『可运行』的权限?
    -u 侦测该档名是否存在且具有『SUID』的属性?
    -g 侦测该档名是否存在且具有『SGID』的属性?
    -k 侦测该档名是否存在且具有『Sticky bit』的属性?
    -s 侦测该档名是否存在且为『非空白文件』?
    3. 两个文件之间的比较,如: test file1 -nt file2
    -nt (newer than)判断 file1 是否比 file2 新
    -ot (older than)判断 file1 是否比 file2 旧
    -ef 判断 file1 与 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode 哩!
    4. 关於两个整数之间的判定,例如 test n1 -eq n2
    -eq 两数值相等 (equal)
    -ne 两数值不等 (not equal)
    -gt n1 大於 n2 (greater than)
    -lt n1 小於 n2 (less than)
    -ge n1 大於等於 n2 (greater than or equal)
    -le n1 小於等於 n2 (less than or equal)
    5. 判定字串的数据
    test -z string 判定字串是否为 0 ?若 string 为空字串,则为 true
    test -n string 判定字串是否非为 0 ?若 string 为空字串,则为 false。
    注: -n 亦可省略
    test str1 = str2 判定 str1 是否等於 str2 ,若相等,则回传 true
    test str1 != str2 判定 str1 是否不等於 str2 ,若相等,则回传 false
    6. 多重条件判定,例如: test -r filename -a -x filename
    -a (and)两状况同时成立!例如 test -r file -a -x file,则 file 同时具有 r 与 x 权限时,才回传 true。
    -o (or)两状况任何一个成立!例如 test -r file -o -x file,则 file 具有 r 或 x 权限时,就可回传 true。
    ! 反相状态,如 test ! -x file ,当 file 不具有 x 时,回传 true

    OK!现在我们就利用 test 来帮我们写几个简单的例子。首先,判断一下,让使用者输入一个档名,我们判断:

    1. 这个文件是否存在,若不存在则给予一个『Filename does not exist』的信息,并中断程序;
    2. 若这个文件存在,则判断他是个文件或目录,结果输出『Filename is regular file』或 『Filename is directory』
    3. 判断一下,运行者的身份对这个文件或目录所拥有的权限,并输出权限数据!

    你可以先自行创作看看,然后再跟底下的结果讨论讨论。注意利用 test 与 && 还有 || 等标志!

    [root@www scripts]# vi sh05.sh
    #!/bin/bash
    # Program:
    #	User input a filename, program will check the flowing:
    #	1.) exist? 2.) file/directory? 3.) file permissions 
    # History:
    # 2005/08/25	VBird	First release
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    
    # 1. 让使用者输入档名,并且判断使用者是否真的有输入字串?
    echo -e "Please input a filename, I will check the filename's type and 
    permission. 
    
    "
    read -p "Input a filename : " filename
    test -z $filename && echo "You MUST input a filename." && exit 0
    # 2. 判断文件是否存在?若不存在则显示信息并结束脚本
    test ! -e $filename && echo "The filename '$filename' DO NOT exist" && exit 0
    # 3. 开始判断文件类型与属性
    test -f $filename && filetype="regulare file"
    test -d $filename && filetype="directory"
    test -r $filename && perm="readable"
    test -w $filename && perm="$perm writable"
    test -x $filename && perm="$perm executable"
    # 4. 开始输出资讯!
    echo "The filename: $filename is a $filetype"
    echo "And the permissions are : $perm"
    

    如果你运行这个脚本后,他会依据你输入的档名来进行检查喔!先看是否存在,再看为文件或目录类型,最后判断权限。 但是你必须要注意的是,由於 root 在很多权限的限制上面都是无效的,所以使用 root 运行这个脚本时, 常常会发现与 ls -l 观察到的结果并不相同!所以,建议使用一般使用者来运行这个脚本试看看。 不过你必须要使用 root 的身份先将这个脚本搬移给使用者就是了,不然一般使用者无法进入 /root 目录的。 很有趣的例子吧!你可以自行再以其他的案例来撰写一下可用的功能呢!


    小标题的图示利用判断符号 [ ]

    除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ] 』(就是中括号啦) 来进行数据的判断呢! 举例来说,如果我想要知道 $HOME 这个变量是否为空的,可以这样做:

    [root@www ~]# [ -z "$HOME" ] ; echo $?
    

    使用中括号必须要特别注意,因为中括号用在很多地方,包括万用字节与正规表示法等等,所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空白字节来分隔喔! 假设我空白键使用『□』符号来表示,那么,在这些地方你都需要有空白键:

    [  "$HOME"  ==  "$MAIL"  ]
    [□"$HOME"□==□"$MAIL"□]
     ↑       ↑  ↑       ↑
    
    Tips:
    你会发现鸟哥在上面的判断式当中使用了两个等号『 == 』。其实在 bash 当中使用一个等号与两个等号的结果是一样的! 不过在一般惯用程序的写法中,一个等号代表『变量的配置』,两个等号则是代表『逻辑判断 (是否之意)』。 由於我们在中括号内重点在於『判断』而非『配置变量』,因此鸟哥建议您还是使用两个等号较佳!
    鸟哥的图示

    上面的例子在说明,两个字串 $HOME 与 $MAIL 是否相同的意思,相当於 test $HOME = $MAIL 的意思啦! 而如果没有空白分隔,例如 [$HOME==$MAIL] 时,我们的 bash 就会显示错误信息了!这可要很注意啊! 所以说,你最好要注意:

    • 在中括号 [] 内的每个组件都需要有空白键来分隔;
    • 在中括号内的变量,最好都以双引号括号起来;
    • 在中括号内的常数,最好都以单或双引号括号起来。

    为什么要这么麻烦啊?直接举例来说,假如我配置了 name="VBird Tsai" ,然后这样判定:

    [root@www ~]# name="VBird Tsai"
    [root@www ~]# [ $name == "VBird" ]
    bash: [: too many arguments
    

    见鬼了!怎么会发生错误啊?bash 还跟我说错误是由於『太多参数 (arguments)』所致! 为什么呢?因为 $name 如果没有使用双引号刮起来,那么上面的判定式会变成:

    [ VBird Tsai == "VBird" ]

    上面肯定不对嘛!因为一个判断式仅能有两个数据的比对,上面 VBird 与 Tsai 还有 "VBird" 就有三个数据! 这不是我们要的!我们要的应该是底下这个样子:

    [ "VBird Tsai" == "VBird" ]

    这可是差很多的喔!另外,中括号的使用方法与 test 几乎一模一样啊~ 只是中括号比较常用在条件判断式 if ..... then ..... fi的情况中就是了。 好,那我们也使用中括号的判断来做一个小案例好了,案例配置如下:

    1. 当运行一个程序的时候,这个程序会让使用者选择 Y 或 N ,
    2. 如果使用者输入 Y 或 y 时,就显示『 OK, continue 』
    3. 如果使用者输入 n 或 N 时,就显示『 Oh, interrupt !』
    4. 如果不是 Y/y/N/n 之内的其他字节,就显示『 I don't know what your choice is 』
    利用中括号、 && 与 || 来继续吧!
    [root@www scripts]# vi sh06.sh
    #!/bin/bash
    # Program:
    # 	This program shows the user's choice
    # History:
    # 2005/08/25	VBird	First release
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    
    read -p "Please input (Y/N): " yn
    [ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
    [ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0
    echo "I don't know what your choice is" && exit 0
    

    由於输入正确 (Yes) 的方法有大小写之分,不论输入大写 Y 或小写 y 都是可以的,此时判断式内就得要有两个判断才行! 由於是任何一个成立即可 (大小或小写的 y) ,所以这里使用 -o (或) 连结两个判断喔! 很有趣吧!利用这个字串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢! 接下来,我们再来谈一些其他有的没有的东西吧!


    小标题的图示Shell script 的默认变量($0, $1...)

    我们知道命令可以带有选项与参数,例如 ls -la 可以察看包含隐藏档的所有属性与权限。那么 shell script 能不能在脚本档名后面带有参数呢?很有趣喔!举例来说,如果你想要重新启动系统登录档的功能,可以这样做:

    [root@www ~]# file /etc/init.d/syslog
    /etc/init.d/syslog: Bourne-Again shell script text executable
    # 使用 file 来查询后,系统告知这个文件是个 bash 的可运行 script 喔!
    [root@www ~]# /etc/init.d/syslog restart
    

    restart 是重新启动的意思,上面的命令可以『重新启动 /etc/init.d/syslog 这支程序』的意思! 唔!那么如果你在 /etc/init.d/syslog 后面加上 stop 呢?没错!就可以直接关闭该服务了!这么神奇啊? 没错啊!如果你要依据程序的运行给予一些变量去进行不同的任务时,本章一开始是使用 read 的功能!但 read 功能的问题是你得要手动由键盘输入一些判断式。如果透过命令后面接参数, 那么一个命令就能够处理完毕而不需要手动再次输入一些变量行为!这样下达命令会比较简单方便啦!

    script 是怎么达成这个功能的呢?其实 script 针对参数已经有配置好一些变量名称了!对应如下:

    /path/to/scriptname  opt1  opt2  opt3  opt4 
           $0             $1    $2    $3    $4
    

    这样够清楚了吧?运行的脚本档名为 $0 这个变量,第一个接的参数就是 $1 啊~ 所以,只要我们在 script 里面善用 $1 的话,就可以很简单的立即下达某些命令功能了!除了这些数字的变量之外, 我们还有一些较为特殊的变量可以在 script 内使用来呼叫这些参数喔!

    • $# :代表后接的参数『个数』,以上表为例这里显示为『 4 』;
    • $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来);
    • $* :代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字节,默认为空白键, 所以本例中代表『 "$1 $2 $3 $4" 』之意。

    那个 $@ 与 $* 基本上还是有所不同啦!不过,一般使用情况下可以直接记忆 $@ 即可! 好了,来做个例子吧~假设我要运行一个可以携带参数的 script ,运行该脚本后萤幕会显示如下的数据:

    • 程序的档名为何?
    • 共有几个参数?
    • 若参数的个数小於 2 则告知使用者参数数量太少
    • 全部的参数内容为何?
    • 第一个参数为何?
    • 第二个参数为何
    [root@www scripts]# vi sh07.sh
    #!/bin/bash
    # Program:
    #	Program shows the script name, parameters...
    # History:
    # 2009/02/17	VBird	First release
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    
    echo "The script name is        ==> $0"
    echo "Total parameter number is ==> $#"
    [ "$#" -lt 2 ] && echo "The number of parameter is less than 2.  Stop here." 
    	&& exit 0
    echo "Your whole parameter is   ==> '$@'"
    echo "The 1st parameter         ==> $1"
    echo "The 2nd parameter         ==> $2"
    

    运行结果如下:

    [root@www scripts]# sh sh07.sh theone haha quot
    The script name is        ==> sh07.sh            <==档名
    Total parameter number is ==> 3                  <==果然有三个参数
    Your whole parameter is   ==> 'theone haha quot' <==参数的内容全部
    The 1st parameter         ==> theone             <==第一个参数
    The 2nd parameter         ==> haha               <==第二个参数
    


    • shift:造成参数变量号码偏移

    除此之外,脚本后面所接的变量是否能够进行偏移 (shift) 呢?什么是偏移啊?我们直接以底下的范例来说明好了, 用范例说明比较好解释!我们将 sh07.sh 的内容稍作变化一下,用来显示每次偏移后参数的变化情况:

    [root@www scripts]# vi sh08.sh
    #!/bin/bash
    # Program:
    #	Program shows the effect of shift function.
    # History:
    # 2009/02/17	VBird	First release
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    
    echo "Total parameter number is ==> $#"
    echo "Your whole parameter is   ==> '$@'"
    shift   # 进行第一次『一个变量的 shift 』
    echo "Total parameter number is ==> $#"
    echo "Your whole parameter is   ==> '$@'"
    shift 3 # 进行第二次『三个变量的 shift 』
    echo "Total parameter number is ==> $#"
    echo "Your whole parameter is   ==> '$@'"
    

    这玩意的运行成果如下:

    [root@www scripts]# sh sh08.sh one two three four five six <==给予六个参数
    Total parameter number is ==> 6   <==最原始的参数变量情况
    Your whole parameter is   ==> 'one two three four five six'
    Total parameter number is ==> 5   <==第一次偏移,看底下发现第一个 one 不见了
    Your whole parameter is   ==> 'two three four five six'
    Total parameter number is ==> 2   <==第二次偏移掉三个,two three four 不见了
    Your whole parameter is   ==> 'five six'
    

    光看结果你就可以知道啦,那个 shift 会移动变量,而且 shift 后面可以接数字,代表拿掉最前面的几个参数的意思。 上面的运行结果中,第一次进行 shift 后他的显示情况是『 one two three four five six』,所以就剩下五个啦!第二次直接拿掉三个,就变成『 two three four five six 』啦! 这样这个案例可以了解了吗?理解了 shift 的功能了吗?


  • 相关阅读:
    Add Two Numbers
    Reverse Linked List II
    Reverse Linked List
    Remove Duplicates from Sorted List
    Remove Duplicates from Sorted List II
    Partition List
    Intersection of Two Linked Lists
    4Sum
    3Sum
    2Sum
  • 原文地址:https://www.cnblogs.com/raphael5200/p/5114820.html
Copyright © 2011-2022 走看看