利用判断符号 [ ]
除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ] 』来进行数据的判断呢! 举例来说,如果我想要知道 $HOME 这个变量是否为空的,可以这样做:
[root@linux ~]# [ -z "$HOME" ]
但使用 [] 要特别注意的是,在上述的每个组件中间都需要有空格键来分隔,假设我空格键使用『□』来表示, 那么,在这些地方你都需要有空格键:
[ "$HOME" == "$MAIL" ]
[□"$HOME"□==□"$MAIL"□]
↑ ↑ ↑ ↑
上面的例子在说明,两个字符串 $HOME 与 $MAIL 是否相同的意思,相当于 test $HOME = $MAIL 的意思啦! 而如果没有空白分隔,例如 [$HOME==$MAIL] 时,我们的 bash 就会显示错误讯息了!这可要很注意啊! 所以说,您最好要注意:
在中括号 [] 内的每个组件都需要有空格键来分隔;
在中括号内的变量,最好都以双引号来设定;
在中括号内的常数,最好都以单或双引号来设定。
举例来说,假如我设定了 name="VBird Tsai" ,然后这样判定:
[root@linux ~]# name="VBird Tsai"
[root@linux ~]# [ $name == "VBird" ]
bash: [: too many arguments
为什么呢?因为 $name 如果没有使用双引号刮起来,那么上面的判定式会变成:
[ 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 is your choise』
利用中括号、 && 与 || 来继续吧!
[root@linux scripts]# vi sh06.sh
#!/bin/bash
# Program:
# This program will show 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 is your choise" && exit 0
很有趣吧!利用这个字符串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢! 接下来,我们再来谈一些其它有的没有的东西吧!
Tips:
为什么判断式里面下达等于要用 == 而不是一个 = 就好了呢?我们在前一章正规表示法里面的 awk 提到, 只有一个 = 用来给予一个变量设定其内容,逻辑判断时,则会给予两个等于, 亦即『比较』而非『设定』的意思~这里要好好的分辨一下喔! ^_^
--------------------------------------------------------------------------------
Shell script 的预设变数($0, $1...)
其实,当我们执行一个 shell script 时,在这个 shell script 里面就已将帮我们做好一些可用的变量了。 举例来说,在不久的将来,您就会发现,当我们要启动一个系统服务时,可能会下达类似这样的指令:
[root@linux ~]# /etc/init.d/crond restart
那是啥玩意儿?呵呵!就是『向 /etc/init.d/crond 这个 script 下达 restart 的指令』, 咦!我们不是都使用 read 来读取使用者输入的变量内容吗?为啥我可以直接在 script 后面接上这个参数? 这是因为 shell script 帮我们设定好一些指定的变量了!变量的对应是这样的:
/path/to/scriptname opt1 opt2 opt3 opt4 ...
$0 $1 $2 $3 $4 ...
这样够清楚了吧?!执行的文件名为 $0 这个变量,第一个接的参数就是 $1 啊~ 所以,只要我们在 script 里面善用 $1 的话,就可以很简单的立即下达某些指令功能了! 好了,来做个例子吧~假设我要执行一个 script ,执行后,该 script 会自动列出自己的档名, 还有后面接的前三个参数,该如何是好?
[root@linux scripts]# vi sh07.sh
#!/bin/bash
# Program:
# The program will show it's name and first 3 parameters.
# History:
# 2005/08/25 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "The script naem is ==>; $0"
[ -n "$1" ] && echo "The 1st paramter is ==>; $1" || exit 0
[ -n "$2" ] && echo "The 2nd paramter is ==>; $2" || exit 0
[ -n "$3" ] && echo "The 3th paramter is ==>; $3" || exit 0
这支程序里面鸟哥加上了一些控制式,亦即利用 && 及 || 来加以判断 $1 ~ $3 是否存在? 若存在才显示,若不存在就中断~执行结果如下:
[root@linux scripts]# sh sh07.sh theone haha quot
The script naem is ==>; sh07.sh
The 1st paramter is ==>; theone
The 2nd paramter is ==>; haha
The 3th paramter is ==>; quot
上面这七的例子都很简单吧?几乎都是利用 bash 的相关功能而已~ 不难啦~底下我们就要使用条件判断式来进行一些分别功能的设定了,好好瞧一瞧先~
--------------------------------------------------------------------------------
条件判断式:
只要讲到『程序』的话,那么条件判断式,亦即是『 if then 』这种判别式肯定一定要学习的! 因为很多时候,我们都必须要依据某些数据来判断程序该如何进行。举例来说,我们在上头不是有练习当使用者输入 Y/N 时,必须要执行不同的讯息输出吗?简单的方式可以利用 && 与 || ,但如果我还想要执行一堆指令呢? 那真的得要 if then 来帮忙啰~底下我们就来聊一聊!
--------------------------------------------------------------------------------
利用 if .... then
这个 if .... then 是最常见的条件判断式了~简单的说,就是当符合某个条件判断的时候, 就予以进行某项工作就是了。我们可以简单的这样看:
if [ 条件判断式 ]; then
当条件判断式成立时,可以进行的指令工作内容;
fi
至于条件判断式的判断方法,与前一小节的介绍相同啊!较特别的是,如果我有多个条件要判别时, 除了 sh06.sh 那个案例,也就是将多个条件写入一个中括号内的情况之外, 我还可以有多个中括号来隔开喔!而括号与括号之间,则以 && 或 || 来隔开,他们的意义是:
&& 代表 AND ;
|| 代表 or ;
所以,在使用中括号的判断式中, && 及 || 就与指令下达的状态不同了。举例来说, sh06.sh 那个例子我可以改写成这样:
[root@linux scripts]# vi sh06-2.sh
#!/bin/bash
# Program:
# This program will show 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
if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
echo "OK, continue"
exit 0
fi
if [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
echo "Oh, interrupt!"
exit 0
fi
echo "I don't know what is your choise" && exit 0
不过,由这个例子看起来,似乎也没有什么了不起吧? sh06.sh 还比较简单呢~ 但是,如果我们考虑底下的状态,您就会知道 if then 的好处了:
if [ 条件判断式 ]; then
当条件判断式成立时,可以进行的指令工作内容;
else
当条件判断式不成立时,可以进行的指令工作内容;
fi
如果考虑更复杂的情况,则可以使用这个语法:
if [ 条件判断式一 ]; then
当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
当条件判断式二成立时,可以进行的指令工作内容;
else
当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi
那我就可以将 sh06-2.sh 改写成这样:
[root@linux scripts]# vi sh06-3.sh
#!/bin/bash
# Program:
# This program will show 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
if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
echo "OK, continue"
elif [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
echo "Oh, interrupt!"
else
echo "I don't know what is your choise"
fi
是否程序变得很简单,而且依序判断,可以避免掉重复判断的状况,这样真的很容易设计程序的啦! ^_^ 好了,那么如果我要侦测你所输入的参数是否为 hello 呢 , 也就是说,如果我想要知道,你在程序后面所接的第一个参数 (就是 $1 啊!) 是否为 hello ,
如果是的话,就显示 "Hello, how are you ?";
如果没有加任何参数,就提示使用者必须要使用的参数下达法;
而如果加入的参数不是 hello ,就提醒使用者仅能使用 hello 为参数。
整个程序的撰写可以是这样的:
[root@linux scripts]# vi sh08.sh
#!/bin/bash
# Program:
# Show "Hello" from $1....
# History:
# 2005/08/28 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
if [ "$1" == "hello" ]; then
echo "Hello, how are you ?"
elif [ "$1" == "" ]; then
echo "You MUST input parameters, ex>; $0 someword"
else
echo "The only parameter is 'hello'"
fi
然后您可以执行这支程序,分别在 $1 的位置输入 hello, 没有输入与随意输入, 就可以看到不同的输出啰~是否还觉得挺简单的啊! ^_^。事实上, 学到这里,也真的很厉害了~好了,底下我们继续来玩一些比较大一点的啰~ 我们在前一章已经学会了 grep 这个好用的玩意儿,那么多学一个叫做 netstat 的指令, 这个指令可以查询到目前主机有开启的网络服务端口口 (service ports), 相关的功能我们会在服务器架设篇继续介绍,这里您只要知道,我可以利用『 netstat -tuln 』来取得目前主机有启动的服务, 而且取得的信息有点像这样:
[root@linux ~]# netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:199 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN
tcp 0 0 :::22 :::* LISTEN
tcp 0 0 :::25 :::* LISTEN
上面的重点是特殊字体的那个部分,那些特殊字体的部分代表的就是 port 啰~ 那么每个 port 代表的意义为何呢?几个常见的 port 与相关网络服务的关系是:
80: WWW
22: ssh
21: ftp
25: mail
那我如何透过 netstat 去侦测我的主机是否有开启这四个主要的网络服务端口口呢? 我可以简单的这样去写这个程序喔:
[root@linux scripts]# vi sh09.sh
#!/bin/bash
# Program:
# Using netstat and grep to detect WWW,SSH,FTP and Mail services.
# History:
# 2005/08/28 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 1. 先作一些告知的动作而已~
echo "Now, the services of your Linux system will be detect!"
echo -e "The www, ftp, ssh, and mail will be detect! "
# 2. 开始进行一些测试的工作,并且也输出一些信息啰!
testing=`netstat -tuln | grep ":80 "`
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
testing=`netstat -tuln | grep ":22 "`
if [ "$testing" != "" ]; then
echo "SSH is running in your system."
fi
testing=`netstat -tuln | grep ":21 "`
if [ "$testing" != "" ]; then
echo "FTP is running in your system."
fi
testing=`netstat -tuln | grep ":25 "`
if [ "$testing" != "" ]; then
echo "Mail is running in your system."
fi
这样又能够一个一个的检查啰~是否很有趣啊! ^_^。接下来,我们再来玩更难一点的。 我们知道可以利用 date 来显示日期与时间,也可以利用 $((计算式)) 来计算数值运算。 另外, date 也可以用来显示自 19710101 以来的『总秒数』 (请自行查阅 man date 及 info date) 。那么,您是否可以撰写一支小程序,用来『计算退伍日期还剩几天?』也就是说:
先让使用者输入他们的退伍日期;
再由现在日期比对退伍日期;
由两个日期的比较来显示『还需要几天』才能够退伍的字样。
似乎挺难的样子?其实也不会啦,利用『 date --date="YYYYMMDD" +%s 』就能够达到我们所想要的啰~如果您已经写完了程序,对照底下的写法试看看:
[root@linux scripts]# vi sh10.sh
#!/bin/bash
# Program:
# Tring to calculate your demobilization date at how many days
# later...
# History:
# 2005/08/29 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 about your demobilization date..."
read -p "Please input your demobilization date (YYYYMMDD ex>;20050401): " date2
# 2. 测试一下,这个输入的内容是否正确?利用正规表示法啰~
date_d=`echo $date2 |grep '[0-9]{8}'`
if [ "$date_d" == "" ]; then
echo "You input the wrong format of date...."
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"
else
declare -i date_h=$(($(($date_total_s-$date_d*60*60*24))/60/60))
echo "You will be demobilized after $date_d days and $date_h hours."
fi
瞧一瞧,这支程序可以帮您计算退伍日期呢~如果是已经退伍的朋友, 还可以知道已经退伍多久了~哈哈!很可爱吧~利用 date 算出自 1971/01/01 以来的总秒数, 再与目前的总秒数来比对,然后以一天的总秒数 (60*60*24) 为基数去计算总日数, 就能够得知两者的差异了~瞧~全部的动作都没有超出我们所学的范围吧~ ^_^ 还能够避免使用者输入错误的数字,所以多了一个正规表示法的判断式呢~ 这个例子比较难,有兴趣想要一探究竟的朋友,可以作一下课后练习题 关于计算生日的那一题喔!~加油!
--------------------------------------------------------------------------------
利用 case ..... esac 判断
上个小节提到的『 if .... then .... fi 』对于变量的判断中, 是以比对的方式来分辨的,如果符合状态就进行某些行为,并且透过较多层次 ( 就是 elif ... ) 的方式来进行多个变量的程序代码撰写,譬如 sh08.sh 那个小程序,就是用这样的方式来的啰。 好,那么万一我有多个既定的变量内容,例如 sh08.sh 当中,我所需要的变量就是 "hello" 及空字符串两个, 那么我只要针对这两个变量来设定状况就好了对吧?!那么可以使用什么方式来设计呢? 呵呵~就用 case ... in .... esac 吧~,他的语法如下:
case $变量名称 in
"第一个变量内容")
程序段
;;
"第二个变量内容")
程序段
;;
*)
不包含第一个变量内容与第二个变量内容的其它程序执行段
exit 1
;;
esac
要注意的是,这个语法是以 case 为开头,而以 esac 为结尾,啥?为何是 esac 呢?想一想,既然 if 的结尾是 fi ,那么 case 的结尾当然就是将 case 倒着写,自然就是 esac 啰~ ^_^,很好记吧~ 另外,每一个变量内容的程序段最后都需要两个分号 (;;) 来代表该程序段落的结束,这挺重要的喔! 至于为何需要有 * 这个变量内容在最后呢?这是因为,如果使用者不是输入变量内容一或二时, 我们可以告知使用者相关的信息啊!举例来说,我们如果将 sh08.sh 改写的话, 他应该会变成这样喔!
[root@linux scripts]# vi sh08-2.sh
#!/bin/bash
# Program:
# Show "Hello" from $1.... by using case .... esac
# History:
# 2005/08/29 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case $1 in
"hello")
echo "Hello, how are you ?"
;;
"")
echo "You MUST input parameters, ex>; $0 someword"
;;
*)
echo "Usage $0 {hello}"
;;
esac
在上面这个 sh08-2.sh 的案例当中,如果你输入『 sh sh08-2.sh test 』来执行, 那么屏幕上就会出现『Usage sh08-2.sh {hello}』的字样,告知执行者仅能够使用 hello 喔~ 这样的方式对于需要某些固定字符串来执行的变量内容就显的更加的方便呢? 这种方式您真的要熟悉喔!这是因为系统的很多服务的启动 scripts 都是使用这种写法的, 举例来说,我们 Linux 的服务启动放置目录是在 /etc/init.d/ 当中,我已经知道里头有个 syslog 的服务,我想要重新启动这个服务,可以这样做:
/etc/init.d/syslog restart
重点是那个 restart 啦~如果您进入 /etc/init.d/syslog 就会看到他使用的是 case 语法, 并且会规定某些既定的变量内容,你可以直接下达 /etc/init.d/syslog , 该 script 就会告知你有哪些后续接的变量可以使用啰~方便吧! ^_^
一般来说,使用『 case $变量 in 』这个语法中,当中的那个 $变量 大致有两种取得的方式:
直接下达式:例如上面提到的,利用『 script.sh variable 』 的方式来直接给予 $1 这个变量的内容,这也是在 /etc/init.d 目录下大多数程序的设计方式。
交互式:透过 read 这个指令来让使用者输入变量的内容。
这么说或许您的感受性还不高,好,我们直接写个程序来玩玩:让使用者能够输入 one, two, three , 并且将使用者的变量显示到屏幕上,如果不是 one, two, three 时,就告知使用者仅有这三种选择。
[root@linux scripts]# vi sh11.sh
#!/bin/bash
# Program:
# Let user input one, two, three and show in screen.
# History:
# 2005/08/29 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 {one|two|three}"
;;
esac
此时,您可以使用『 sh sh11.sh two 』的方式来下达指令,就可以收到相对应的响应了。 上面使用的是直接下达的方式,而如果使用的是交互式时,那么将上面第 10, 11 行的 "#" 拿掉, 并将 12 行加上批注 (#),就可以让使用者输入参数啰~这样是否很有趣啊?!
除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ] 』来进行数据的判断呢! 举例来说,如果我想要知道 $HOME 这个变量是否为空的,可以这样做:
[root@linux ~]# [ -z "$HOME" ]
但使用 [] 要特别注意的是,在上述的每个组件中间都需要有空格键来分隔,假设我空格键使用『□』来表示, 那么,在这些地方你都需要有空格键:
[ "$HOME" == "$MAIL" ]
[□"$HOME"□==□"$MAIL"□]
↑ ↑ ↑ ↑
上面的例子在说明,两个字符串 $HOME 与 $MAIL 是否相同的意思,相当于 test $HOME = $MAIL 的意思啦! 而如果没有空白分隔,例如 [$HOME==$MAIL] 时,我们的 bash 就会显示错误讯息了!这可要很注意啊! 所以说,您最好要注意:
在中括号 [] 内的每个组件都需要有空格键来分隔;
在中括号内的变量,最好都以双引号来设定;
在中括号内的常数,最好都以单或双引号来设定。
举例来说,假如我设定了 name="VBird Tsai" ,然后这样判定:
[root@linux ~]# name="VBird Tsai"
[root@linux ~]# [ $name == "VBird" ]
bash: [: too many arguments
为什么呢?因为 $name 如果没有使用双引号刮起来,那么上面的判定式会变成:
[ 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 is your choise』
利用中括号、 && 与 || 来继续吧!
[root@linux scripts]# vi sh06.sh
#!/bin/bash
# Program:
# This program will show 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 is your choise" && exit 0
很有趣吧!利用这个字符串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢! 接下来,我们再来谈一些其它有的没有的东西吧!
Tips:
为什么判断式里面下达等于要用 == 而不是一个 = 就好了呢?我们在前一章正规表示法里面的 awk 提到, 只有一个 = 用来给予一个变量设定其内容,逻辑判断时,则会给予两个等于, 亦即『比较』而非『设定』的意思~这里要好好的分辨一下喔! ^_^
--------------------------------------------------------------------------------
Shell script 的预设变数($0, $1...)
其实,当我们执行一个 shell script 时,在这个 shell script 里面就已将帮我们做好一些可用的变量了。 举例来说,在不久的将来,您就会发现,当我们要启动一个系统服务时,可能会下达类似这样的指令:
[root@linux ~]# /etc/init.d/crond restart
那是啥玩意儿?呵呵!就是『向 /etc/init.d/crond 这个 script 下达 restart 的指令』, 咦!我们不是都使用 read 来读取使用者输入的变量内容吗?为啥我可以直接在 script 后面接上这个参数? 这是因为 shell script 帮我们设定好一些指定的变量了!变量的对应是这样的:
/path/to/scriptname opt1 opt2 opt3 opt4 ...
$0 $1 $2 $3 $4 ...
这样够清楚了吧?!执行的文件名为 $0 这个变量,第一个接的参数就是 $1 啊~ 所以,只要我们在 script 里面善用 $1 的话,就可以很简单的立即下达某些指令功能了! 好了,来做个例子吧~假设我要执行一个 script ,执行后,该 script 会自动列出自己的档名, 还有后面接的前三个参数,该如何是好?
[root@linux scripts]# vi sh07.sh
#!/bin/bash
# Program:
# The program will show it's name and first 3 parameters.
# History:
# 2005/08/25 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "The script naem is ==>; $0"
[ -n "$1" ] && echo "The 1st paramter is ==>; $1" || exit 0
[ -n "$2" ] && echo "The 2nd paramter is ==>; $2" || exit 0
[ -n "$3" ] && echo "The 3th paramter is ==>; $3" || exit 0
这支程序里面鸟哥加上了一些控制式,亦即利用 && 及 || 来加以判断 $1 ~ $3 是否存在? 若存在才显示,若不存在就中断~执行结果如下:
[root@linux scripts]# sh sh07.sh theone haha quot
The script naem is ==>; sh07.sh
The 1st paramter is ==>; theone
The 2nd paramter is ==>; haha
The 3th paramter is ==>; quot
上面这七的例子都很简单吧?几乎都是利用 bash 的相关功能而已~ 不难啦~底下我们就要使用条件判断式来进行一些分别功能的设定了,好好瞧一瞧先~
--------------------------------------------------------------------------------
条件判断式:
只要讲到『程序』的话,那么条件判断式,亦即是『 if then 』这种判别式肯定一定要学习的! 因为很多时候,我们都必须要依据某些数据来判断程序该如何进行。举例来说,我们在上头不是有练习当使用者输入 Y/N 时,必须要执行不同的讯息输出吗?简单的方式可以利用 && 与 || ,但如果我还想要执行一堆指令呢? 那真的得要 if then 来帮忙啰~底下我们就来聊一聊!
--------------------------------------------------------------------------------
利用 if .... then
这个 if .... then 是最常见的条件判断式了~简单的说,就是当符合某个条件判断的时候, 就予以进行某项工作就是了。我们可以简单的这样看:
if [ 条件判断式 ]; then
当条件判断式成立时,可以进行的指令工作内容;
fi
至于条件判断式的判断方法,与前一小节的介绍相同啊!较特别的是,如果我有多个条件要判别时, 除了 sh06.sh 那个案例,也就是将多个条件写入一个中括号内的情况之外, 我还可以有多个中括号来隔开喔!而括号与括号之间,则以 && 或 || 来隔开,他们的意义是:
&& 代表 AND ;
|| 代表 or ;
所以,在使用中括号的判断式中, && 及 || 就与指令下达的状态不同了。举例来说, sh06.sh 那个例子我可以改写成这样:
[root@linux scripts]# vi sh06-2.sh
#!/bin/bash
# Program:
# This program will show 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
if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
echo "OK, continue"
exit 0
fi
if [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
echo "Oh, interrupt!"
exit 0
fi
echo "I don't know what is your choise" && exit 0
不过,由这个例子看起来,似乎也没有什么了不起吧? sh06.sh 还比较简单呢~ 但是,如果我们考虑底下的状态,您就会知道 if then 的好处了:
if [ 条件判断式 ]; then
当条件判断式成立时,可以进行的指令工作内容;
else
当条件判断式不成立时,可以进行的指令工作内容;
fi
如果考虑更复杂的情况,则可以使用这个语法:
if [ 条件判断式一 ]; then
当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
当条件判断式二成立时,可以进行的指令工作内容;
else
当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi
那我就可以将 sh06-2.sh 改写成这样:
[root@linux scripts]# vi sh06-3.sh
#!/bin/bash
# Program:
# This program will show 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
if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
echo "OK, continue"
elif [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
echo "Oh, interrupt!"
else
echo "I don't know what is your choise"
fi
是否程序变得很简单,而且依序判断,可以避免掉重复判断的状况,这样真的很容易设计程序的啦! ^_^ 好了,那么如果我要侦测你所输入的参数是否为 hello 呢 , 也就是说,如果我想要知道,你在程序后面所接的第一个参数 (就是 $1 啊!) 是否为 hello ,
如果是的话,就显示 "Hello, how are you ?";
如果没有加任何参数,就提示使用者必须要使用的参数下达法;
而如果加入的参数不是 hello ,就提醒使用者仅能使用 hello 为参数。
整个程序的撰写可以是这样的:
[root@linux scripts]# vi sh08.sh
#!/bin/bash
# Program:
# Show "Hello" from $1....
# History:
# 2005/08/28 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
if [ "$1" == "hello" ]; then
echo "Hello, how are you ?"
elif [ "$1" == "" ]; then
echo "You MUST input parameters, ex>; $0 someword"
else
echo "The only parameter is 'hello'"
fi
然后您可以执行这支程序,分别在 $1 的位置输入 hello, 没有输入与随意输入, 就可以看到不同的输出啰~是否还觉得挺简单的啊! ^_^。事实上, 学到这里,也真的很厉害了~好了,底下我们继续来玩一些比较大一点的啰~ 我们在前一章已经学会了 grep 这个好用的玩意儿,那么多学一个叫做 netstat 的指令, 这个指令可以查询到目前主机有开启的网络服务端口口 (service ports), 相关的功能我们会在服务器架设篇继续介绍,这里您只要知道,我可以利用『 netstat -tuln 』来取得目前主机有启动的服务, 而且取得的信息有点像这样:
[root@linux ~]# netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:199 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN
tcp 0 0 :::22 :::* LISTEN
tcp 0 0 :::25 :::* LISTEN
上面的重点是特殊字体的那个部分,那些特殊字体的部分代表的就是 port 啰~ 那么每个 port 代表的意义为何呢?几个常见的 port 与相关网络服务的关系是:
80: WWW
22: ssh
21: ftp
25: mail
那我如何透过 netstat 去侦测我的主机是否有开启这四个主要的网络服务端口口呢? 我可以简单的这样去写这个程序喔:
[root@linux scripts]# vi sh09.sh
#!/bin/bash
# Program:
# Using netstat and grep to detect WWW,SSH,FTP and Mail services.
# History:
# 2005/08/28 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 1. 先作一些告知的动作而已~
echo "Now, the services of your Linux system will be detect!"
echo -e "The www, ftp, ssh, and mail will be detect! "
# 2. 开始进行一些测试的工作,并且也输出一些信息啰!
testing=`netstat -tuln | grep ":80 "`
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
testing=`netstat -tuln | grep ":22 "`
if [ "$testing" != "" ]; then
echo "SSH is running in your system."
fi
testing=`netstat -tuln | grep ":21 "`
if [ "$testing" != "" ]; then
echo "FTP is running in your system."
fi
testing=`netstat -tuln | grep ":25 "`
if [ "$testing" != "" ]; then
echo "Mail is running in your system."
fi
这样又能够一个一个的检查啰~是否很有趣啊! ^_^。接下来,我们再来玩更难一点的。 我们知道可以利用 date 来显示日期与时间,也可以利用 $((计算式)) 来计算数值运算。 另外, date 也可以用来显示自 19710101 以来的『总秒数』 (请自行查阅 man date 及 info date) 。那么,您是否可以撰写一支小程序,用来『计算退伍日期还剩几天?』也就是说:
先让使用者输入他们的退伍日期;
再由现在日期比对退伍日期;
由两个日期的比较来显示『还需要几天』才能够退伍的字样。
似乎挺难的样子?其实也不会啦,利用『 date --date="YYYYMMDD" +%s 』就能够达到我们所想要的啰~如果您已经写完了程序,对照底下的写法试看看:
[root@linux scripts]# vi sh10.sh
#!/bin/bash
# Program:
# Tring to calculate your demobilization date at how many days
# later...
# History:
# 2005/08/29 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 about your demobilization date..."
read -p "Please input your demobilization date (YYYYMMDD ex>;20050401): " date2
# 2. 测试一下,这个输入的内容是否正确?利用正规表示法啰~
date_d=`echo $date2 |grep '[0-9]{8}'`
if [ "$date_d" == "" ]; then
echo "You input the wrong format of date...."
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"
else
declare -i date_h=$(($(($date_total_s-$date_d*60*60*24))/60/60))
echo "You will be demobilized after $date_d days and $date_h hours."
fi
瞧一瞧,这支程序可以帮您计算退伍日期呢~如果是已经退伍的朋友, 还可以知道已经退伍多久了~哈哈!很可爱吧~利用 date 算出自 1971/01/01 以来的总秒数, 再与目前的总秒数来比对,然后以一天的总秒数 (60*60*24) 为基数去计算总日数, 就能够得知两者的差异了~瞧~全部的动作都没有超出我们所学的范围吧~ ^_^ 还能够避免使用者输入错误的数字,所以多了一个正规表示法的判断式呢~ 这个例子比较难,有兴趣想要一探究竟的朋友,可以作一下课后练习题 关于计算生日的那一题喔!~加油!
--------------------------------------------------------------------------------
利用 case ..... esac 判断
上个小节提到的『 if .... then .... fi 』对于变量的判断中, 是以比对的方式来分辨的,如果符合状态就进行某些行为,并且透过较多层次 ( 就是 elif ... ) 的方式来进行多个变量的程序代码撰写,譬如 sh08.sh 那个小程序,就是用这样的方式来的啰。 好,那么万一我有多个既定的变量内容,例如 sh08.sh 当中,我所需要的变量就是 "hello" 及空字符串两个, 那么我只要针对这两个变量来设定状况就好了对吧?!那么可以使用什么方式来设计呢? 呵呵~就用 case ... in .... esac 吧~,他的语法如下:
case $变量名称 in
"第一个变量内容")
程序段
;;
"第二个变量内容")
程序段
;;
*)
不包含第一个变量内容与第二个变量内容的其它程序执行段
exit 1
;;
esac
要注意的是,这个语法是以 case 为开头,而以 esac 为结尾,啥?为何是 esac 呢?想一想,既然 if 的结尾是 fi ,那么 case 的结尾当然就是将 case 倒着写,自然就是 esac 啰~ ^_^,很好记吧~ 另外,每一个变量内容的程序段最后都需要两个分号 (;;) 来代表该程序段落的结束,这挺重要的喔! 至于为何需要有 * 这个变量内容在最后呢?这是因为,如果使用者不是输入变量内容一或二时, 我们可以告知使用者相关的信息啊!举例来说,我们如果将 sh08.sh 改写的话, 他应该会变成这样喔!
[root@linux scripts]# vi sh08-2.sh
#!/bin/bash
# Program:
# Show "Hello" from $1.... by using case .... esac
# History:
# 2005/08/29 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case $1 in
"hello")
echo "Hello, how are you ?"
;;
"")
echo "You MUST input parameters, ex>; $0 someword"
;;
*)
echo "Usage $0 {hello}"
;;
esac
在上面这个 sh08-2.sh 的案例当中,如果你输入『 sh sh08-2.sh test 』来执行, 那么屏幕上就会出现『Usage sh08-2.sh {hello}』的字样,告知执行者仅能够使用 hello 喔~ 这样的方式对于需要某些固定字符串来执行的变量内容就显的更加的方便呢? 这种方式您真的要熟悉喔!这是因为系统的很多服务的启动 scripts 都是使用这种写法的, 举例来说,我们 Linux 的服务启动放置目录是在 /etc/init.d/ 当中,我已经知道里头有个 syslog 的服务,我想要重新启动这个服务,可以这样做:
/etc/init.d/syslog restart
重点是那个 restart 啦~如果您进入 /etc/init.d/syslog 就会看到他使用的是 case 语法, 并且会规定某些既定的变量内容,你可以直接下达 /etc/init.d/syslog , 该 script 就会告知你有哪些后续接的变量可以使用啰~方便吧! ^_^
一般来说,使用『 case $变量 in 』这个语法中,当中的那个 $变量 大致有两种取得的方式:
直接下达式:例如上面提到的,利用『 script.sh variable 』 的方式来直接给予 $1 这个变量的内容,这也是在 /etc/init.d 目录下大多数程序的设计方式。
交互式:透过 read 这个指令来让使用者输入变量的内容。
这么说或许您的感受性还不高,好,我们直接写个程序来玩玩:让使用者能够输入 one, two, three , 并且将使用者的变量显示到屏幕上,如果不是 one, two, three 时,就告知使用者仅有这三种选择。
[root@linux scripts]# vi sh11.sh
#!/bin/bash
# Program:
# Let user input one, two, three and show in screen.
# History:
# 2005/08/29 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 {one|two|three}"
;;
esac
此时,您可以使用『 sh sh11.sh two 』的方式来下达指令,就可以收到相对应的响应了。 上面使用的是直接下达的方式,而如果使用的是交互式时,那么将上面第 10, 11 行的 "#" 拿掉, 并将 12 行加上批注 (#),就可以让使用者输入参数啰~这样是否很有趣啊?!