中国建设银行官网首页网站,英语培训机构网站建设策划书,网站建设有那些内容,百度信息流广告怎么收费【BASH】回顾与知识点梳理 十八 十八. 条件判断式18.1 利用 test 指令的测试功能文件类型判断文件权限侦测两个文件之间的比较两个整数之间的判定判定字符串的数据多重条件判定 18.2 利用判断符号 [ ]18.3 Shell script 的默认参数($0, $1...)shift#xff1a;造成参数变量号码… 【BASH】回顾与知识点梳理 十八 十八. 条件判断式18.1 利用 test 指令的测试功能文件类型判断文件权限侦测两个文件之间的比较两个整数之间的判定判定字符串的数据多重条件判定 18.2 利用判断符号 [ ]18.3 Shell script 的默认参数($0, $1...)shift造成参数变量号码偏移 18.4 利用 if .... then单层、简单条件判断式多重、复杂条件判断式 18.5 利用 case ..... esac 判断18.6 利用 function 功能 该系列目录 -- 【BASH】回顾与知识点梳理目录
十八. 条件判断式
在第五章中我们提到过 $? 这个变量所代表的意义 此外也透过 及 || 来作为前一个指令执行回传值对于后一个指令是否要进行的依据。第五章的讨论中如果想要判断一个目录是否存在 当时我们使用的是 ls 这个指令搭配数据流重导向最后配合 $? 来决定后续的指令进行与否。 但是否有更简单的方式可以来进行『条件判断』呢有的那就是『 test 』这个指令。
18.1 利用 test 指令的测试功能
当我要检测系统上面某些文件或者是相关的属性时利用 test 这个指令来工作真是好用得不得了举例来说我要检查 /dmtsai 是否存在时使用
[dmtsaistudy ~]$ test -e /dmtsai执行结果并不会显示任何讯息但最后我们可以透过 $? 或 及||来展现整个结果呢 例如我们在将上面的例子改写成这样
[dmtsaistudy ~]$ test -e /dmtsai echo exist || echo Not exist
Not exist 结果显示不存在啊最终的结果可以告知我们是『exist』还是『Not exist』呢那我知道 -e 是测试一个『东西』在不在如果还想要测试一下该档名是啥玩意儿时还有哪些标志可以来判断的呢呵呵有底下这些东西喔
文件类型判断
如 test -e filename 表示存在否
测试的标志代表意义-e该『档名』是否存在(常用)-f该『档名』是否存在且为文件(file)(常用)-d该『文件名』是否存在且为目录(directory)(常用)-b该『档名』是否存在且为一个 block device 装置-c该『档名』是否存在且为一个 character device 装置-S该『档名』是否存在且为一个 Socket 文件-p该『档名』是否存在且为一个 FIFO (pipe) 文件-L该『档名』是否存在且为一个连结档
文件权限侦测
如 test -r filename 表示可读否 (但 root 权限常有例外)
测试的标志代表意义-r侦测该档名是否存在且具有『可读』的权限-w侦测该档名是否存在且具有『可写』的权限-x侦测该档名是否存在且具有『可执行』的权限-u侦测该文件名是否存在且具有『SUID』的属性-g侦测该文件名是否存在且具有『SGID』的属性-k侦测该文件名是否存在且具有『Sticky bit』的属性-s侦测该档名是否存在且为『非空白文件』
两个文件之间的比较
如 test file1 -nt file2
测试的标志代表意义-nt(newer than)判断 file1 是否比 file2 新-ot(older than)判断 file1 是否比 file2 旧-ef判断 file1 与 file2 是否为同一文件可用在判断 hard link 的判定上。 主要意义在判定两个文件是否均指向同一个 inode 哩
两个整数之间的判定
例如 test n1 -eq n2
测试的标志代表意义-eq两数值相等 (equal)-ne两数值不等 (not equal)-gtn1 大于 n2 (greater than)-ltn1 小于 n2 (less than)-gen1 大于等于 n2 (greater than or equal)-len1 小于等于 n2 (less than or equal)
判定字符串的数据
测试的标志代表意义test -z string判定字符串长度是否为 0 若 string 为空字符串则为 truetest -n string判定字符串长度是否非为 0 若 string 为空字符串则为 false。注 -n 亦可省略test str1 str2判定 str1 是否等于 str2 若相等则回传 truetest str1 ! str2判定 str1 是否不等于 str2 若相等则回传 false
多重条件判定
例如 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 来帮我们写几个简单的例子。首先判断一下让使用者输入一个档名我们判断
这个文件是否存在若不存在则给予一个『Filename does not exist』的讯息并中断程序若这个文件存在则判断他是个文件或目录结果输出『Filename is regular file』或 『Filename is directory』判断一下执行者的身份对这个文件或目录所拥有的权限并输出权限数据
你可以先自行创作看看然后再跟底下的结果讨论讨论。注意利用 test 与 还有 || 等标志
[dmtsaistudy 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 filenames 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} filetyperegulare file
test -d ${filename} filetypedirectory
test -r ${filename} permreadable
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 观察到的结果并不相同所以建议使用一般使用者来执行这个脚本试看看。
18.2 利用判断符号 [ ]
除了我们很喜欢使用的 test 之外其实我们还可以利用判断符号『 [ ] 』(就是中括号啦) 来进行数据的判断呢 举例来说如果我想要知道 ${HOME} 这个变量是否为空的可以这样做
[dmtsaistudy ~]$ [ -z ${HOME} ] ; echo $?使用中括号必须要特别注意因为中括号用在很多地方包括通配符与正规表示法等等所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时必须要注意中括号的两端需要有空格符来分隔喔 假设我空格键使用『□』符号来表示那么在这些地方你都需要有空格键
[ $HOME $MAIL ]
[□$HOME□□$MAIL□]↑ ↑ ↑ ↑你会发现鸟哥在上面的判断式当中使用了两个等号『 』。其实在 bash 当中使用一个等号与两个等号的结果是一样的 不过在一般惯用程序的写法中一个等号代表『变量的设定』两个等号则是代表『逻辑判断 (是与否之意)』。 由于我们在中括号内重点在于『判断』而非『设定变量』因此鸟哥建议您还是使用两个等号较佳 上面的例子在说明两个字符串${HOME}与${MAIL}是否相同的意思相当于 test ${HOME} ${MAIL} 的意思啦 而如果没有空白分隔例如[${HOME}${MAIL}]时我们的 bash 就会显示错误讯息了这可要很注意啊 所以说你最好要注意
在中括号 [] 内的每个组件都需要有空格键来分隔在中括号内的变数最好都以双引号括号起来在中括号内的常数最好都以单或双引号括号起来。
为什么要这么麻烦啊直接举例来说假如我设定了 name“VBird Tsai” 然后这样判定
[yurqnode-135 bin]$ nameVBird Tsai
[yurqnode-135 bin]$ [ ${name} VBird ]
-bash: [: too many arguments见鬼了怎么会发生错误啊bash 还跟我说错误是由于『太多参数 (arguments)』所致 为什么呢因为 ${name} 如果没有使用双引号刮起来那么上面的判定式会变成
[ VBird Tsai VBird ]上面肯定不对嘛因为一个判断式仅能有两个数据的比对上面 VBird 与 Tsai 还有 “VBird” 就有三个资料 这不是我们要的我们要的应该是底下这个样子
[ VBird Tsai VBird ]这可是差很多的喔另外中括号的使用方法与 test 几乎一模一样啊 只是中括号比较常用在条件判断式if ..... then ..... fi的情况中就是了。 好那我们也使用中括号的判断来做一个小案例好了案例设定如下
当执行一个程序的时候这个程序会让用户选择 Y 或 N 如果用户输入 Y 或 y 时就显示『 OK, continue 』如果用户输入 n 或 N 时就显示『 Oh, interrupt 』如果不是 Y/y/N/n 之内的其他字符就显示『 I don’t know what your choice is 』
[yurqnode-135 bin]$ cat choice.sh
#!/bin/bash
# Program:
# User input a choice[y(Y)/n(N)], program will print the flowing:
# 1.) OK, continue 2.) Oh, interrupt 3.) I dont know what your choice is
# History:
# 2023/08/09 yurq First releasePATH$PATH:~/bin
export PATHecho -e input a choice [y(Y)/n(N)]\n
read -p choice [y(Y)/n(N)]: choice
[ $choice Y -o $choice y ] echo OK, continue exit 0|| \
[ $choice N -o $choice n ] echo Oh, interrupt ||\
echo I dont know what your choice is由于输入正确 (Yes) 的方法有大小写之分不论输入大写 Y 或小写 y 都是可以的此时判断式内就得要有两个判断才行 由于是任何一个成立即可 (大写或小写的 y) 所以这里使用 -o (或) 连结两个判断喔 很有趣吧利用这个字符串判别的方法我们就可以很轻松的将使用者想要进行的工作分门别类呢 接下来我们再来谈一些其他有的没有的东西吧
18.3 Shell script 的默认参数($0, $1…)
我们知道指令可以带有选项与参数例如 ls -la 可以察看包含隐藏文件的所有属性与权限。那么 shell script 能不能在脚本档名后面带有参数呢很有趣喔举例来说如果你想要重新启动系统的网络可以这样做
[dmtsaistudy ~]$ file /etc/init.d/network
/etc/init.d/network: Bourne-Again shell script, ASCII text executable
# 使用 file 来查询后系统告知这个文件是个 bash 的可执行 script 喔
[dmtsaistudy ~]$ /etc/init.d/network restartrestart 是重新启动的意思上面的指令可以『重新启动 /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” 』之意每个变量是独立的(用双引号括起来)$*代表『 “$1c$2c$3c$4” 』其中 c 为分隔字符默认为空格键 所以本例中代表『 “$1 $2 $3 $4” 』之意。
那个$与$*基本上还是有所不同啦不过一般使用情况下可以直接记忆 $ 即可 好了来做个例子吧假设我要执行一个可以携带参数的 script 执行该脚本后屏幕会显示如下的数据 程序的文件名为何共有几个参数若参数的个数小于 2 则告知使用者参数数量太少全部的参数内容为何第一个参数为何第二个参数为何 [dmtsaistudy 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}执行结果如下
[dmtsaistudy 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 的内容稍作变化一下用来显示每次偏移后参数的变化情况
[dmtsaistudy 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 $这玩意的执行成果如下
[dmtsaistudy 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 的相关功能而已 不难啦底下我们就要使用条件判断式来进行一些分别功能的设定了好好瞧一瞧先
18.4 利用 if … then
这个if .... then是最常见的条件判断式了简单的说就是当符合某个条件判断的时候 就予以进行某项工作就是了。这个 if … then 的判断还有多层次的情况我们分别介绍如下
单层、简单条件判断式
如果你只有一个判断式要进行那么我们可以简单的这样看
if [ 条件判断式 ]; then当条件判断式成立时可以进行的指令工作内容
fi 将 if 反过来写就成为 fi 啦结束 if 之意至于条件判断式的判断方法与前一小节的介绍相同啊较特别的是如果我有多个条件要判别时除了 ans_yn.sh 那个案例所写的也就是『将多个条件写入一个中括号内的情况』之外 我还可以有多个中括号来隔开喔而括号与括号之间则以 或 || 来隔开他们的意义是 代表 and || 代表 or
所以在使用中括号的判断式中 及 || 就与指令下达的状态不同了。举例来说 ans_yn.sh 里面的判断式可以这样修改
[ ${yn} Y -o ${yn} y ]
#上式可替换为
[ ${yn} Y ] || [ ${yn} y ]之所以这样改很多人是习惯问题很多人则是喜欢一个中括号仅有一个判别式的原因。好了 现在我们来将 ans_yn.sh 这个脚本修改成为 if … then 的样式来看看
[dmtsaistudy bin]$ cp ans_yn.sh ans_yn-2.sh 用复制来修改的比较快
[dmtsaistudy bin]$ vim ans_yn-2.sh
#!/bin/bash
# Program:
# This program shows the users 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
if [ ${yn} Y ] || [ ${yn} y ]; thenecho OK, continueexit 0
fi
if [ ${yn} N ] || [ ${yn} n ]; thenecho Oh, interrupt!exit 0
fi
echo I dont know what your choice is exit 0不过由这个例子看起来似乎也没有什么了不起吧原本的 ans_yn.sh 还比较简单呢 但是如果以逻辑概念来看其实上面的范例中我们使用了两个条件判断呢明明仅有一个 ${yn} 的变量为何需要进行两次比对呢 此时多重条件判断就能够来测试测试啰
多重、复杂条件判断式
在同一个数据的判断中如果该数据需要进行多种不同的判断时应该怎么作举例来说上面的ans_yn.sh 脚本中我们只要进行一次 ${yn} 的判断就好 (仅进行一次 if )不想要作多次 if 的判断。此时你就得要知道底下的语法了
# 一个条件判断分成功进行与失败进行 (else)
if [ 条件判断式 ]; then当条件判断式成立时可以进行的指令工作内容
else当条件判断式不成立时可以进行的指令工作内容
fi如果考虑更复杂的情况则可以使用这个语法
# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行
if [ 条件判断式一 ]; then当条件判断式一成立时可以进行的指令工作内容
elif [ 条件判断式二 ]; then当条件判断式二成立时可以进行的指令工作内容
else当条件判断式一与二均不成立时可以进行的指令工作内容
fi你得要注意的是 elif 也是个判断式因此出现 elif 后面都要接 then 来处理但是 else 已经是最后的没有成立的结果了 所以 else 后面并没有 then 喔好我们来将 ans_yn-2.sh 改写成这样
[dmtsaistudy bin]$ cp ans_yn-2.sh ans_yn-3.sh
[dmtsaistudy bin]$ vim ans_yn-3.sh
#!/bin/bash
# Program:
# This program shows the users 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
if [ ${yn} Y ] || [ ${yn} y ]; thenecho OK, continue
elif [ ${yn} N ] || [ ${yn} n ]; thenecho Oh, interrupt!
elseecho I dont know what your choice is
fi是否程序变得很简单而且依序判断可以避免掉重复判断的状况这样真的很容易设计程序的啦^_^ 好了让我们再来进行另外一个案例的设计。一般来说如果你不希望用户由键盘输入额外的数据时 可以使用上一节提到的参数功能 ($1)让用户在下达指令时就将参数带进去 现在我们想让用户输入『 hello 』这个关键词时利用参数的方法可以这样依序设计
判断 $1 是否为 hello如果是的话就显示 “Hello, how are you ?”如果没有加任何参数就提示使用者必须要使用的参数下达法而如果加入的参数不是 hello 就提醒使用者仅能使用 hello 为参数。
整个程序的撰写可以是这样的
[dmtsaistudy bin]$ vim hello-2.sh
#!/bin/bash
# Program:
# Check $1 is equal to hello
# History:
# 2015/07/16 VBird First release
PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
if [ ${1} hello ]; thenecho Hello, how are you ?
elif [ ${1} ]; thenecho You MUST input parameters, ex {${0} someword}
elseecho The only parameter is hello, ex {${0} hello}
fi然后你可以执行这支程序分别在 $1 的位置输入 hello, 没有输入与随意输入 就可以看到不同的输出啰是否还觉得挺简单的啊 ^_^。事实上 学到这里也真的很厉害了好了底下我们继续来玩一些比较大一点的计划啰
我们在前面已经学会了 grep 这个好用的玩意儿那么多学一个叫做 netstat 的指令这个指令可以查询到目前主机有开启的网络服务端口口 (service ports) 相关的功能我们会在服务器架设篇继续介绍这里你只要知道我可以利用『 netstat -tuln 』来取得目前主机有启动的服务 而且取得的信息有点像这样
[dmtsaistudy ~]$ netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
...
udp 0 0 127.0.0.1:323 0.0.0.0:*
udp6 0 0 :::123 :::*
udp6 0 0 ::1:323 :::*
#封包格式 本地 IP:埠口 远程 IP:埠口 是否监听上面的重点是『Local Address (本地主机的 IP 与端口口对应)』那个字段他代表的是本机所启动的网络服务 IP 的部分说明的是该服务位于那个接口上若为 127.0.0.1 则是仅针对本机开放若是0.0.0.0 或 ::: 则代表对整个 Internet 开放 (更多信息请参考服务器架设篇的介绍)。 每个埠口 (port) 都有其特定的网络服务几个常见的 port 与相关网络服务的关系是
80: WWW22: ssh21: ftp25: mail111: RPC(远程过程调用)631: CUPS(打印服务功能)
假设我的主机有兴趣要侦测的是比较常见的 port 21, 22, 25 及 80 时那我如何透过 netstat 去侦测我的主机是否有开启这四个主要的网络服务端口口呢由于每个服务的关键词都是接在冒号『 : 』后面 所以可以藉由撷取类似『 :80 』来侦测的那我就可以简单的这样去写这个程序喔
[dmtsaistudy bin]$ vim netstat.sh
#!/bin/bash
# Program:
# Using netstat and grep to detect WWW,SSH,FTP and Mail services.
# 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 Now, I will detect your Linux servers services!
echo -e The www, ftp, ssh, and mail(smtp) will be detect! \n
# 2. 开始进行一些测试的工作并且也输出一些信息啰
testfile/dev/shm/netstat_checking.txt
netstat -tuln ${testfile} # 先转存数据到内存当中不用一直执行 netstat
testing$(grep :80 ${testfile}) # 侦测看 port 80 在否
if [ ${testing} ! ]; then
echo WWW is running in your system.
fi
testing$(grep :22 ${testfile}) # 侦测看 port 22 在否
if [ ${testing} ! ]; then
echo SSH is running in your system.
fi
testing$(grep :21 ${testfile}) # 侦测看 port 21 在否
if [ ${testing} ! ]; then
echo FTP is running in your system.
fi
testing$(grep :25 ${testfile}) # 侦测看 port 25 在否
if [ ${testing} ! ]; then
echo Mail is running in your system.
finetstat -nltu|awk {print $4}|grep :|sed s/.*://g|sort|uniq 实际执行这支程序你就可以看到你的主机有没有启动这些服务啦是否很有趣呢 条件判断式还可以搞的更复杂举例来说在台湾当兵是国民应尽的义务不过在当兵的时候总是很想要退伍的那你能不能写个脚本程序来跑让用户输入他的退伍日期让你去帮他计算还有几天才退伍
由于日期是要用相减的方式来处置所以我们可以透过使用 date 显示日期与时间将他转为由1970-01-01 累积而来的秒数 透过秒数相减来取得剩余的秒数后再换算为日数即可。整个脚本的制作流程有点像这样
先让使用者输入他们的退伍日期再由现在日期比对退伍日期由两个日期的比较来显示『还需要几天』才能够退伍的字样。
似乎挺难的样子其实也不会啦利用『 date --date“YYYYMMDD” %s 』转成秒数后接下来的动作就容易的多了如果你已经写完了程序对照底下的写法试看看
[dmtsaistudy bin]$ vim cal_retired.sh
#!/bin/bash
# Program:
# You input your demobilization date, I calculate how many days before you demobilize.
# 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 This program will try to calculate :
echo How many days before your demobilization date...
read -p Please input your demobilization date (YYYYMMDD ex20150716): date2
# 2. 测试一下这个输入的内容是否正确利用正规表示法啰
date_d$(echo ${date2} |grep [0-9]\{8\}) # 看看是否有八个数字
if [ ${date_d} ]; then
echo You input the wrong date format....
exit 1
fi
# 3. 开始计算日期啰
declare -i date_dem$(date --date${date2} %s) # 退伍日期秒数
declare -i date_now$(date %s) # 现在日期秒数
declare -i date_total_s$((${date_dem}-${date_now})) # 剩余秒数统计
declare -i date_d$((${date_total_s}/60/60/24)) # 转为日数
if [ ${date_total_s} -lt 0 ]; then # 判断是否已退伍echo You had been demobilization before: $((-1*${date_d})) ago
elsedeclare -i date_h$(($((${date_total_s}-${date_d}*60*60*24))/60/60))echo You will demobilize after ${date_d} days and ${date_h} hours.
fi瞧一瞧这支程序可以帮你计算退伍日期呢如果是已经退伍的朋友还可以知道已经退伍多久了哈哈很可爱吧脚本中的 date_d 变量宣告那个 /60/60/24 是来自于一天的总秒数 (24 小时60 分60 秒) 。瞧全部的动作都没有超出我们所学的范围吧 ^_^ 还能够避免用户输入错误的数字所以多了一个正规表示法的判断式呢 这个例子比较难有兴趣想要一探究竟的朋友可以作一下课后练习题 关于计算生日的那一题喔加油
18.5 利用 case … esac 判断
上个小节提到的『 if … then … fi 』对于变量的判断是以『比对』的方式来分辨的 如果符合状态就进行某些行为并且透过较多层次 (就是 elif …) 的方式来进行多个变量的程序代码撰写譬如 hello-2.sh 那个小程序就是用这样的方式来撰写的啰。 好那么万一我有多个既定的变量内容例如 hello-2.sh 当中我所需要的变量就是 “hello” 及空字符串两个 那么我只要针对这两个变量来设定状况就好了对吧那么可以使用什么方式来设计呢呵呵就用 case ... in .... esac 吧他的语法如下
case $变量名称 in 关键词为 case 还有变数前有钱字号第一个变量内容) 每个变量内容建议用双引号括起来关键词则为小括号 )程序段;; 每个类别结尾使用两个连续的分号来处理第二个变量内容)程序段;;*) 最后一个变量内容都会用 * 来代表所有其他值不包含第一个变量内容与第二个变量内容的其他程序执行段exit 1;;
esac 最终的 case 结尾『反过来写』思考一下要注意的是这个语法以case(实际案例之意) 为开头结尾自然就是将 case 的英文反过来写就成为esac啰 不会很难背啦另外每一个变量内容的程序段最后都需要两个分号 (;;) 来代表该程序段落的结束这挺重要的喔 至于为何需要有*这个变量内容在最后呢这是因为如果用户不是输入变量内容一或二时 我们可以告知用户相关的信息啊废话少说我们拿 hello-2.sh 的案例来修改一下他应该会变成这样喔
[dmtsaistudy bin]$ vim hello-3.sh
#!/bin/bash
# Program:
# Show Hello from $1.... by using case .... esac
# History:
# 2015/07/16 VBird First release
PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case ${1} inhello)echo Hello, how are you ?;;)echo You MUST input parameters, ex {${0} someword};;*) # 其实就相当于通配符0~无穷多个任意字符之意echo Usage ${0} {hello};;
esac在上面这个 hello-3.sh 的案例当中如果你输入『 sh hello-3.sh test 』来执行 那么屏幕上就会出现『Usage hello-3.sh {hello}』的字样告知执行者仅能够使用 hello 喔 这样的方式对于需要某些固定字符串来执行的变量内容就显的更加的方便呢 这种方式你真的要熟悉喔这是因为早期系统的很多服务的启动 scripts 都是使用这种写法的 (CentOS 6.x 以前)。 虽然 CentOS 7 已经使用systemd不过仍有数个服务是放在 /etc/init.d/ 目录下喔例如有个名为 netconsole 的服务在该目录下 那么你想要重新启动该服务是可以这样做的 (请注意要成功执行还是得要具有 root 身份才行一般账号能执行但不会成功)
/etc/init.d/netconsole restart重点是那个 restart 啦如果你使用『 less /etc/init.d/netconsole 』去查阅一下就会看到他使用的是case 语法 并且会规定某些既定的变量内容你可以直接下达 /etc/init.d/netconsole 该 script 就会告知你有哪些后续接的变量可以使用啰方便吧 ^_^
一般来说使用『 case $变量 in 』这个语法中当中的那个『 $变量 』大致有两种取得的方式
直接下达式例如上面提到的利用『 script.sh variable 』 的方式来直接给予 $1 这个变量的内容这也是在 /etc/init.d 目录下大多数程序的设计方式。交互式透过 read 这个指令来让用户输入变量的内容。
这么说或许你的感受性还不高好我们直接写个程序来玩玩让使用者能够输入 one, two, three 并且将用户的变量显示到屏幕上如果不是 one, two, three 时就告知使用者仅有这三种选择。
[dmtsaistudy bin]$ vim show123.sh
#!/bin/bash
# Program:
# This script only accepts the flowing parameter: one, two or three.
# History:
# 2015/07/17 VBird First release
PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo This program will print your selection !
# read -p Input your choice: choice # 暂时取消可以替换
# case ${choice} in # 暂时取消可以替换
case ${1} in # 现在使用可以用上面两行替换one)echo Your choice is ONE;;two)echo Your choice is TWO;;three)echo Your choice is THREE;;*)echo Usage ${0} {one|two|three};;
esac此时你可以使用『 sh show123.sh two 』的方式来下达指令就可以收到相对应的响应了。 上面使用的是直接下达的方式而如果使用的是交互式时那么将上面第 10, 11 行的 “#” 拿掉并将 12 行加上批注 (#)就可以让使用者输入参数啰这样是否很有趣啊
18.6 利用 function 功能
什么是『函数 (function)』功能啊简单的说其实 函数可以在 shell script 当中做出一个类似自定义执行指令的东西最大的功能是可以简化我们很多的程序代码举例来说上面的 show123.sh 当中每个输入结果 one, two, three 其实输出的内容都一样啊那么我就可以使用 function 来简化了 function 的语法是这样的
function fname() {程序段
}那个 fname 就是我们的自定义的执行指令名称而程序段就是我们要他执行的内容了。要注意的是因为 shell script 的执行方式是由上而下由左而右 因此在 shell script 当中的 function 的设定一定要在程序的最前面 这样才能够在执行时被找到可用的程序段喔 (这一点与传统程序语言差异相当大初次接触的朋友要小心) 好我们将 show123.sh 改写一下自定义一个名为 printit 的函数来使用喔
[dmtsaistudy bin]$ vim show123-2.sh
#!/bin/bash
# Program:
# Use function to repeat information.
# History:
# 2015/07/17 VBird First release
PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit(){echo -n Your choice is # 加上 -n 可以不断行继续在同一行显示
}
echo This program will print your selection !
case ${1} inone)printit; echo ${1} | tr a-z A-Z # 将参数做大小写转换;;two)printit; echo ${1} | tr a-z A-Z;;three)printit; echo ${1} | tr a-z A-Z;;*)echo Usage ${0} {one|two|three};;
esac以上面的例子来说鸟哥做了一个函数名称为 printit 所以当我在后续的程序段里面 只要执行printit 的话就表示我的 shell script 要去执行『 function printit … 』 里面的那几个程序段落啰当然啰上面这个例子举得太简单了所以你不会觉得 function 有什么好厉害的 不过如果某些程序代码一再地在 script 当中重复时这个 function 可就重要的多啰 不但可以简化程序代码而且可以做成类似『模块』的玩意儿真的很棒啦 建议读者可以使用类似 vim 的编辑器到 /etc/init.d/ 目录下去查阅一下你所看到的文件 并且自行追踪一下每个文件的执行情况相信会更有心得 另外 function 也是拥有内建变量的他的内建变量与 shell script 很类似 函数名称代表示 $0 而后续接的变量也是以 $1, $2… 来取代的 这里很容易搞错喔因为『 function fname() { 程序段 } 』内的 $0, $1… 等等与 shell script 的 $0 是不同的。以上面 show123-2.sh 来说假如我下达『 sh show123-2.sh one 』 这表示在 shell script 内的 $1 为 “one” 这个字符串。但是在 printit() 内的 $1 则与这个 one 无关。 我们将上面的例子再次的改写一下让你更清楚
[dmtsaistudy bin]$ vim show123-3.sh
#!/bin/bash
# Program:
# Use function to repeat information.
# History:
# 2015/07/17 VBird First release
PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit(){echo Your choice is ${1} # 这个 $1 必须要参考底下指令的下达
}
echo This program will print your selection !
case ${1} inone)printit 1 # 请注意 printit 指令后面还有接参数;;two)printit 2;;three)printit 3;;*)echo Usage ${0} {one|two|three};;
esac在上面的例子当中如果你输入『 sh show123-3.sh one 』就会出现『 Your choice is 1 』的字样 为什么是 1 呢因为在程序段落当中我们是写了『 printit 1 』那个 1 就会成为 function 当中的 $1 喔 这样是否理解呢 function 本身其实比较困难一点如果你还想要进行其他的撰写的话。 不过我们仅是想要更加了解 shell script 而已所以这里看看即可了解原理就好啰 ^_^
该系列目录 -- 【BASH】回顾与知识点梳理目录