参考:

http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#script_be

http://www.runoob.com/linux/linux-tutorial.html

12.3 善用判断式

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

12.3.1 利用test 指令的测试功能

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

[dmtsai@study ~]$ test -e /dmtsai

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

[dmtsai@study ~]$ 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 与&& 还有|| 等标志!

[dmtsai@study bin]$ vim file_perm.sh
#!/bin/bash
# Program:
# User input a filename, program will check the flowing:
# 1.) exist? 2.) file/directory? 3.) file permissions
# History:
# 2015/07/16 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. \n\n"
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 for you are : ${perm}"

如果你执行这个脚本后,他会依据你输入的档名来进行检查喔!先看是否存在,再看为档案或目录类型,最后判断权限。但是你必须要注意的是,由于root在很多权限的限制上面都是无效的,所以使用root执行这个脚本时,常常会发现与ls -l观察到的结果并不相同!所以,建议使用一般使用者来执行这个脚本试看看。\

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

[dmtsai@study ~]$ [ -z "${HOME}" ] ; echo $?

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

[ "$HOME" == "$MAIL" ]
[□"$HOME"□==□"$MAIL"□]
↑ ↑ ↑ ↑

Tips你会发现鸟哥在上面的判断式当中使用了两个等号『 == 』。其实在bash 当中使用一个等号与两个等号的结果是一样的!不过在一般惯用程式的写法中,一个等号代表『变数的设定』,两个等号则是代表『逻辑判断(是与否之意)』。由于我们在中括号内重点在于『判断』而非『设定变数』,因此鸟哥建议您还是使用两个等号较佳!

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

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

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

[dmtsai@study ~]$ name="VBird Tsai"
[dmtsai@study ~]$ [ ${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 』

利用中括号、 && 与|| 来继续吧!

[dmtsai@study bin]$ vim ans_yn.sh
#!/bin/bash
# Program:
# This program shows the user's choice
# History:
# 2015/07/16 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 (或) 连结两个判断喔!很有趣吧!利用这个字串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢!接下来,我们再来谈一些其他有的没有的东西吧!

12.3.3 Shell script 的预设变数($0, $1...)

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

[dmtsai@study ~]$ file /etc/init.d/network
/etc/init.d/network: Bourne-Again shell script, ASCII text executable
#使用file来查询后,系统告知这个档案是个bash的可执行script喔!
[dmtsai@study ~]$ /etc/init.d/network restart

restart是重新启动的意思,上面的指令可以『重新启动/etc/init.d/network这支程式』的意思!唔!那么如果你在/etc/init.d/network后面加上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" 』之意,每个变数是独立的(用双引号括起来);
  • "$*" :代表『 "$1 c $2 c $3 c $4" 』,其中c为分隔字元,预设为空白键,所以本例中代表『 "$1 $2 $3 $4" 』之意。

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

Tips有点怪异的是, $@ 与"$@" 的结果并不一样喔!当你输入的参数内带有双引号(") 时,建议还是得要使用"$@" 来带入脚本中, 否则双引号会被取消,这样执行结果的差异会相当大喔!尤其是像『 ./script one "a to b" 』这种仅有两个参数,但是参数内还有空白字元的, 最容易出现莫名的问题喔!

  • 程式的档名为何?
  • 共有几个参数?
  • 若参数的个数小于2 则告知使用者参数数量太少
  • 全部的参数内容为何?
  • 第一个参数为何?
  • 第二个参数为何
[dmtsai@study bin]$ vim how_paras.sh
#!/bin/bash
# Program:
# Program shows the script name, parameters...
# History:
# 2015/07/16 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}"

执行结果如下:

[dmtsai@study bin]$ sh how_paras.sh theone haha quot
The script name is ==> how_paras.sh <==档名
Total parameter number is ==> 3 <==果然有三个参数
Your whole parameter is == > 'theone haha quot' <==参数的内容全部
The 1st parameter ==> theone <==第一个参数
The 2nd parameter ==> haha <==第二个参数
  • shift:造成参数变数号码偏移

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

[dmtsai@study bin]$ vim shift_paras.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 ==> '$@'"

这玩意的执行成果如下:

[dmtsai@study bin]$ sh shift_paras.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的功能了吗?

上面这几个例子都很简单吧?几乎都是利用bash 的相关功能而已~ 不难啦~底下我们就要使用条件判断式来进行一些分别功能的设定了,好好瞧一瞧先~

鸟哥的 Linux 私房菜Shell Scripts篇(三)的更多相关文章

  1. 鸟哥的 Linux 私房菜Shell Scripts篇(一)

    参考: http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#script_be http://www.runoob.com/lin ...

  2. 鸟哥的 Linux 私房菜Shell Scripts篇(四)

    12.4 条件判断式 只要讲到『程式』的话,那么条件判断式,亦即是『 if then 』这种判别式肯定一定要学习的!因为很多时候,我们都必须要依据某些资料来判断程式该如何进行.举例来说,我们在上头的a ...

  3. 鸟哥的 Linux 私房菜Shell Scripts篇(二)

    参考: http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#script_be http://www.runoob.com/lin ...

  4. 拒绝从入门到放弃_《鸟哥的 Linux 私房菜 — 基础学习篇(第三版)》必读目录

    目录 目录 前言 关于这本书 必看知识点 最后 前言 相信部分刚进入这个行业的新同学会对一个问题感到疑惑,为什么从培训学校出来的学员不被欢迎? 这里记录下一些我个人的看法(博主也曾有面试新员工的经历) ...

  5. 每周一书-《鸟哥的Linux私房菜基础学习篇(第四版)》台湾原版,你想要吗?

     首先说明,本周活动有效时间为2016年10月19日到2016年10月31日.   目在介绍这本书之前,首先要感谢QQ号为:1084830483(路在远方),来自哈尔滨工程大学的同学赠送给玄魂工作室的 ...

  6. 【Linux】鸟哥的Linux私房菜基础学习篇整理(一)

    最近,一直在写PPC的模拟器和汇编器,也在做设计.所以重新看了看<鸟哥的Linux私房菜>,还是有好多命令不太熟悉.就打算写几篇blog记下来. 1. nl [-bnw] filename ...

  7. 《鸟哥的Linux私房菜-基础学习篇(第三版)》(三)

    第2章 Linxu怎样学习         1. Linux当前的应用角色 当前的Linux常见的应用可略分为企业应用和个人应用双方面. 首先谈了企业环境的利用. 1)网络server. 2)关键任务 ...

  8. 鸟哥的Linux私房菜 基础学习篇读书笔记(9):Linux磁盘与文件系统管理(2)

    上一篇文章主要从理论上分析了Linux的Ext2文件系统.这一篇主要解说怎样查看Linux的文件系统的容量以及解说Linux文件系统中的连接文件. 能够通过df和du命令来查看磁盘与文件夹的容量.df ...

  9. 《鸟哥的Linux私房菜-基础学习篇(第三版)》(四)

    第3章 主机规划与磁盘分区        1. Linux与硬件的搭配        首先谈了认识计算机的硬件配置. 然后谈了选择与Linux搭配的主机配置. 在Linuxserver中,内存的重要性 ...

随机推荐

  1. Intellij-配置JDK版本和编译版本

    (1) (2) (3) (4)

  2. SQL 必知必会·笔记<18>管理事务处理

    事务处理是一种机制,用来管理必须成批执行的SQL操作,保证数据库不包含不完整的操作结果.利用事务处理,可以保证一组操作不会中途停止,它们要 么完全执行,要么完全不执行(除非明确指示).如果没有错误发生 ...

  3. Java并发编程笔记之ThreadLocal内存泄漏探究

    使用 ThreadLocal 不当可能会导致内存泄露,是什么原因导致的内存泄漏呢? 我们首先看一个例子,代码如下: /** * Created by cong on 2018/7/14. */ pub ...

  4. 在2018年如何优雅的开发一个typescript语言的npm包?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由小明plus发表 很多时候,我们可能想要用 typescript 语言来创建一些模块,并提交到 npm 供别人使用, 那么在 2018 ...

  5. linux图形化客户端

    很多服务器都用linux 但这些linux都是没有图形化界面的, 一般也不建议在服务器上装图形化界面 我们都知道,维护linux,大部分都是使用命令 那么,为什么不能开发一个应用程序, 把图形化操作转 ...

  6. PM2来部署nodejs服务器永久开启

    pm2 日常使用   1. pm2 是什么? 日常开发中需要启动一个node项目,需要用npm run …,,如果终端被关掉,程序也就自动停止,有时候几个项目一起跑起来,好几个终端开着,个人不太喜欢, ...

  7. using的几种用法

    1.using指令.using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间 例如:using System; 一般都会出现在*.cs中.   2.using ...

  8. c# 正则格式化文本防止SQL注入

    /// <summary> /// 格式化文本(防止SQL注入) /// </summary> /// <param name="str">&l ...

  9. When should you use a class vs a struct in C++?

    Question: In what scenarios is it better to use a struct vs a class in C++? Answer: The only differe ...

  10. php中mysql和mysqli的总结

    首先php—mysql 是 php 操作 mysql 资料库最原始的的拓展 而php—mysqli,字母i代表的 Improvement ,提更了相对进阶的功能. 推荐学习和使用mysqli mysq ...