只要讲到“程序”的话,那么条件判断式,亦即是“ if then ”这种判别式肯定一定要学习的! 因为很多时候,我们都必须要依据某些数据来判断程序该如何进行。举例来说,我们在上头的 [ans_yn.sh] 讨论输入回应的范例中不是有练习当使用者输入 Y/N 时,必须要执行不同的讯息输出吗?简单的方式可以利用 && 与 || ,但如果我还想要执行一堆指令呢?那真的得要 if then 来帮忙啰~下面我们就来聊一聊!
12.4.1 利用 if .... then
这个 if .... then 是最常见的条件判断式了~简单的说,就是当符合某个条件判断的时候, 就予以进行某项工作就是了。这个 if ... then 的判断还有多层次的情况!我们分别介绍如下:
单层、简单条件判断式
如果你只有一个判断式要进行,那么我们可以简单的这样看:
if [ 条件判断式 ]; then当条件判断式成立时,可以进行的指令工作内容;fi<==将 if反过来写,就成为fi啦!结束if之意!
[dmtsai@study bin]$ vim hello-2.sh#!/bin/bash# Program:# Check $1 is equal to "hello"# History:# 2015/07/16 VBird First releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport PATHif [ "${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
[dmtsai@study 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 releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport PATH# 1\. 先作一些告知的动作而已~echo"Now, I will detect your Linux server's services!"echo-e"The www, ftp, ssh, and mail(smtp) will be detect! \n"# 2\. 开始进行一些测试的工作,并且也输出一些信息啰!testfile=/dev/shm/netstat_checking.txtnetstat-tuln> ${testfile} # 先转存数据到内存当中!不用一直执行 netstattesting=$(grep":80 " ${testfile})# 侦测看 port 80 在否?if [ "${testing}"!="" ]; thenecho"WWW is running in your system."fitesting=$(grep":22 " ${testfile})# 侦测看 port 22 在否?if [ "${testing}"!="" ]; thenecho"SSH is running in your system."fitesting=$(grep":21 " ${testfile})# 侦测看 port 21 在否?if [ "${testing}"!="" ]; thenecho"FTP is running in your system."fitesting=$(grep":25 " ${testfile})# 侦测看 port 25 在否?if [ "${testing}"!="" ]; thenecho"Mail is running in your system."fi
由于日期是要用相减的方式来处置,所以我们可以通过使用 date 显示日期与时间,将他转为由 1970-01-01 累积而来的秒数, 通过秒数相减来取得剩余的秒数后,再换算为日数即可。整个脚本的制作流程有点像这样:
先让使用者输入他们的退伍日期;
再由现在日期比对退伍日期;
由两个日期的比较来显示“还需要几天”才能够退伍的字样。
似乎挺难的样子?其实也不会啦,利用“ date --date="YYYYMMDD" +%s ”转成秒数后,接下来的动作就容易的多了!如果你已经写完了程序,对照下面的写法试看看:
[dmtsai@study 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 releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport 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 ex>20150716): "date2# 2\. 测试一下,这个输入的内容是否正确?利用正则表达式啰~date_d=$(echo ${date2} |grep '[0-9]\{8\}') # 看看是否有八个数字if [ "${date_d}"=="" ]; thenecho"You input the wrong date format...."exit1fi# 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
上个小节提到的“ if .... then .... fi ”对于变量的判断是以“比对”的方式来分辨的, 如果符合状态就进行某些行为,并且通过较多层次 (就是 elif ...) 的方式来进行多个变量的程序码撰写,譬如 [hello-2.sh] 那个小程序,就是用这样的方式来撰写的啰。 好,那么万一我有多个既定的变量内容,例如 hello-2.sh 当中,我所需要的变量就是 "hello" 及空字串两个, 那么我只要针对这两个变量来设置状况就好了,对吧?那么可以使用什么方式来设计呢?呵呵~就用 case ... in .... esac 吧~,他的语法如下:
case $变量名称 in<==关键字为case,还有变量前有钱字号"第一个变量内容")<==每个变量内容建议用双引号括起来,关键字则为小括号)程序段;;<==每个类别结尾使用两个连续的分号来处理!"第二个变量内容")程序段;;*)<==最后一个变量内容都会用*来代表所有其他值不包含第一个变量内容与第二个变量内容的其他程序执行段exit1;;esac<==最终的 case 结尾!“反过来写”思考一下!
要注意的是,这个语法以 case (实际案例之意) 为开头,结尾自然就是将 case 的英文反过来写!就成为 esac 啰! 不会很难背啦!另外,每一个变量内容的程序段最后都需要两个分号 (;;) 来代表该程序段落的结束,这挺重要的喔! 至于为何需要有 * 这个变量内容在最后呢?这是因为,如果使用者不是输入变量内容一或二时, 我们可以告知使用者相关的信息啊!废话少说,我们拿 hello-2.sh 的案例来修改一下,他应该会变成这样喔:
[dmtsai@study bin]$ vim hello-3.sh#!/bin/bash# Program:# Show "Hello" from $1.... by using case .... esac# History:# 2015/07/16 VBird First releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport PATHcase ${1} in"hello")echo"Hello, how are you ?";;"")echo"You MUST input parameters, ex> {${0} someword}";;*)#其实就相当于万用字符,0~无穷多个任意字符之意!echo"Usage ${0} {hello}";;esac
这么说或许你的感受性还不高,好,我们直接写个程序来玩玩:让使用者能够输入 one, two, three , 并且将使用者的变量显示到屏幕上,如果不是 one, two, three 时,就告知使用者仅有这三种选择。
[dmtsai@study bin]$ vim show123.sh#!/bin/bash# Program:# This script only accepts the flowing parameter: one, two or three.# History:# 2015/07/17 VBird First releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport PATHecho"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 行加上注解 (#),就可以让使用者输入参数啰~这样是否很有趣啊?
12.4.3 利用 function 功能
什么是“函数 (function)”功能啊?简单的说,其实, 函数可以在 shell script 当中做出一个类似自订执行指令的东西,最大的功能是, 可以简化我们很多的程序码~举例来说,上面的 show123.sh 当中,每个输入结果 one, two, three 其实输出的内容都一样啊~那么我就可以使用 function 来简化了! function 的语法是这样的:
[dmtsai@study bin]$ vim show123-2.sh#!/bin/bash# Program:# Use function to repeat information.# History:# 2015/07/17 VBird First releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport PATHfunctionprintit(){echo-n"Your choice is "# 加上 -n 可以不断行继续在同一行显示}echo"This program will print your selection !"case ${1} in"one")**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 可就重要的多啰~ 不但可以简化程序码,而且可以做成类似“模块”的玩意儿,真的很棒啦!
Tips 建议读者可以使用类似 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 无关。 我们将上面的例子再次的改写一下,让你更清楚!
[dmtsai@study bin]$ vim show123-3.sh#!/bin/bash# Program:# Use function to repeat information.# History:# 2015/07/17 VBird First releasePATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/binexport PATHfunctionprintit(){echo"Your choice is ${1}"# 这个 $1 必须要参考下面指令的下达}echo"This program will print your selection !"case ${1} in"one")**printit1**#请注意,printit指令后面还有接参数!;;"two")**printit2**;;"three")**printit3**;;*)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 而已,所以,这里看看即可~了解原理就好啰~ ^_^