学习 Shell Scripts
本文数据主要针对 Fedora Core 4 的系统进行说明, Fedora Core 1 主要是由 Red Hat Linux 9 改版而来, 这个 Red Hat Linux 9 并不是当前大家听到的 RHEL 喔!那是在 RHEL 出现之前的产品,基本上是在 2003 年以前的作品了!Fedora Core 4 则是在 2005 年 6 月份发布,使用的内核是 2.6.11 版,当时是很红的一个作品!只是生命周期太短,所以用这个 Fedora 系列来介绍 Server, 当时的决定确实有点莫名其妙了...
建议您前往本站查找最新版本的 Linux distribution 文章来阅读,比较不会浪费时间。那为何还需要编辑 Fedora Core 4 的数据呢? 鸟哥只想要做个自己曾经撰写过的文档内容保存而已啰! ^_^!最新文章请前往鸟站首页查阅啰!
如果您真的很想要走信息这条路,并且想要好好的管理好属于您的主机,那么,别说鸟哥不告诉您, Shell Scripts 真的是必须要学习的一项课题呢!基本上, shell script 有点像是早期的批量档, 亦即是将一些指令汇整起来一次运行,但是 Shell script 拥有更强大的功能,那就是, 他可以进行类似程序 (program) 的撰写,并且,不需要经过编译 (compiler) 就能够运行, 真的很方便。加上,我们可通过 shell script 来简化我们日常的工作管理, 而且,整个 Linux 环境中,一些服务 (services) 的启动都是通过 shell script 的, 如果您对于 script 不了解,嘿嘿!发生问题时,可真是会求助无门喔! 所以,好好的学一学他吧!
[root@linux ~]# mkdir scripts; cd scripts [root@linux scripts]# vi sh01.sh #!/bin/bash # Program: # This program is used to show "Hello World !" in screen. # History: # 2005/08/23 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH echo -e "Hello World ! \a \n" exit 0在我们这个章节当中,请将所有的撰写的 script 放置到您家目录的 ~/scripts 这个目录内, 比较好管理啦!上面的写法当中,我主要将整个程序的撰写分成数段,大致是这样:
[root@linux scripts]# sh sh01.sh
Hello World !
您会看到屏幕是这样,而且应该还会听到『咚』的一声,为什么呢?还记得前一章提到的
printf 吧?用 echo 接着那些特殊的按键也可以发生同样的事情~
不过, echo 必须要加上 -e 的参数才行!
呵呵!在您写完这个小 script 之后,您就可以大声的说:『我也会写程序了』!哈哈!
很简单有趣吧~ ^_^[root@linux scripts]# vi sh02.sh #!/bin/bash # Program: # Let user keyin their first and last name, and show their full name. # History: # 2005/08/23 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH read -p "Please input your first name: " firstname read -p "Please input your last name: " lastname echo -e "\nYour full name is: $firstname $lastname"将上面这个 sh02.sh 运行一下,你就能够发现用户自己输入的变量可以被取用的哩! 很不错吧!加油!
[root@linux scripts]# vi sh03.sh #!/bin/bash # Program: # User can keyin filename to touch 3 new files. # History: # 2005/08/23 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH # 1. 让用户输入文件名称,并取得 fileuser 这个变量; echo -e "I will use 'touch' command to create 3 files." read -p "Please input the filename what you want: " fileuser # 2. 为了避免用户随意按 Enter ,利用变量功能分析文件名是否有设置? filename=${fileuser:-"filename"} # 3. 开始利用 date 指令来取得所需要的文件名了; date1=`date --date='2 days ago' +%Y%m%d` date2=`date --date='1 days ago' +%Y%m%d` date3=`date +%Y%m%d` file1="$filename""$date1" file2="$filename""$date2" file3="$filename""$date3" # 4. 将文件名创建吧! touch $file1 touch $file2 touch $file3我通过一些简单的动作,这些动作都可以在 bash 那一章里面找到, 包括小指令 (`) 的取得消息、变量的设置功能、变量的累加以及利用 touch 指令辅助! 如果您开始运行这个 sh03.sh 之后,你可以进行两次输入,一次直接按 [Enter] 来查阅文件名是啥? 一次可以输入一些字符,这样来判断你的文件喔!关于 date 的指令应用,请 man date 吧! ^_^
[root@linux scripts]# vi sh04.sh #!/bin/bash # Program: # User can input 2 integer to cross by! # History: # 2005/08/23 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH echo -e "You SHOULD input 2 number, I will cross them! \n" read -p "first number: " firstnu read -p "second number: " secnu total=$(($firstnu*$secnu)) echo -e "\nThe number $firstnu x $secnu is ==> $total"在数字的运算上,我们可以使用『 declare -i total=$firstnu*$secnu 』 也可以使用上面的方式来进行!基本上,鸟哥比较建议使用这样的方式来进行运算:
[root@linux scripts]# nu=$((13%3)); echo $nu
1
这样了解了吧?!多多学习与应用喔! ^_^
[root@linux ~]# test -e /dmtsai
运行结果并不会显示任何消息,但最后我们可以通过 $? 或 && 及 || 来展现整个结果呢!
例如我们在将上面的例子改写成这样:
[root@linux ~]# test -e /dmtsai && echo "exist" || echo "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 | |
-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 | 判断 file2 与 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 |
[root@linux scripts]# vi sh05.sh #!/bin/bash # Program: # Let user input a filename, the program will search the filename # 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 "The program will show you that filename is exist which input by you.\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 permission are : $perm"很有趣的例子吧!您可以自行再以其他的案例来撰写一下可用的功能呢!
[root@linux ~]# [ -z "$HOME" ]
但使用 [] 要特别注意的是,在上述的每个组件中间都需要有空白键来分隔,假设我空白键使用『□』来表示,
那么,在这些地方你都需要有空白键:[ "$HOME" == "$MAIL" ] [□"$HOME"□==□"$MAIL"□] ↑ ↑ ↑ ↑上面的例子在说明,两个字符串 $HOME 与 $MAIL 是否相同的意思,相当于 test $HOME = $MAIL 的意思啦! 而如果没有空白分隔,例如 [$HOME==$MAIL] 时,我们的 bash 就会显示错误消息了!这可要很注意啊! 所以说,您最好要注意:
[root@linux ~]# name="VBird Tsai" [root@linux ~]# [ $name == "VBird" ] bash: [: too many arguments为什么呢?因为 $name 如果没有使用双引号刮起来,那么上面的判定式会变成:
[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 choice" && exit 0很有趣吧!利用这个字符串判别的方法,我们就可以很轻松的将用户想要进行的工作分门别类呢! 接下来,我们再来谈一些其他有的没有的东西吧!
[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 name is ==> $0" [ -n "$1" ] && echo "The 1st parameter is ==> $1" || exit 0 [ -n "$2" ] && echo "The 2nd parameter is ==> $2" || exit 0 [ -n "$3" ] && echo "The 3th parameter is ==> $3" || exit 0这支程序里面鸟哥加上了一些控制式,亦即利用 && 及 || 来加以判断 $1 ~ $3 是否存在? 若存在才显示,若不存在就中断~运行结果如下:
[root@linux scripts]# sh sh07.sh theone haha quot
The script name is ==> sh07.sh
The 1st paramter is ==> theone
The 2nd paramter is ==> haha
The 3th paramter is ==> quot
上面这7个例子都很简单吧?几乎都是利用 bash 的相关功能而已~
不难啦~底下我们就要使用条件判断式来进行一些分别功能的设置了,好好瞧一瞧先~
if [ 条件判断式 ]; then 当条件判断式成立时,可以进行的指令工作内容; fi至于条件判断式的判断方法,与前一小节的介绍相同啊!较特别的是,如果我有多个条件要判别时, 除了 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 choice" && 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 choice" fi是否程序变得很简单,而且依序判断,可以避免掉重复判断的状况,这样真的很容易设计程序的啦! ^_^ 好了,那么如果我要侦测你所输入的参数是否为 hello 呢 , 也就是说,如果我想要知道,你在程序后面所接的第一个参数 (就是 $1 啊!) 是否为 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 与相关网络服务的关系是:
[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! \n" # 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) 。那么,您是否可以撰写一支小程序,用来『计算退伍日期还剩几天?』也就是说:
[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 $变量名称 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 的服务,我想要重新启动这个服务,可以这样做:
[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 行加上注解 (#),就可以让用户输入参数啰~这样是否很有趣啊?!
function fname() { 程序段 }那个 fname 就是我们的自订的运行指令名称~而程序段就是我们要他运行的内容了。 要注意的是,在 shell script 当中, function 的设置一定要在程序的最前面, 这样才能够在运行时被找到可用的程序段喔!好~我们将 sh11.sh 改写一下:
[root@linux scripts]# vi sh11-2.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 function printit(){ echo -n "Your choice is " } 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 {one|two|three}" ;; esac以上面的例子来说,我做了一个函数名称为 printit ,所以,当我在后续的程序段里面, 只要运行 printit 的话,就表示我的 shell script 要去运行『 function printit .... 』 里面的那几个程序段落啰! 当然啰,上面这个例子举得太简单了,所以您不会觉得 function 有什么好厉害的, 不过,如果某些代码一再地在 script 当中重复时,这个 function 可就重要的多啰~ 不但可以简化代码,而且可以做成类似『模块』的玩意儿,真的很棒啦!
[root@linux scripts]# vi sh11-3.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 function printit(){ echo "Your choice is $1" } echo "This program will print your selection !" case $1 in "one") printit 1 ;; "two") printit 2 ;; "three") printit 3 ;; *) echo "Usage {one|two|three}" ;; esac在上面的例子当中,如果您输入『 sh sh11-3.sh one 』就会出现『 Your choice is 1 』的字样~ 为什么是 1 呢?因为在程序段落当中,我们是写了『 printit 1 』那个 1 就会成为 function 当中的 $1 喔~ 这样是否理解呢? function 本身其实比较困难一点,如果您还想要进行其他的撰写的话。 不过,我们仅是想要更加了解 shell script 而已,所以,这里看看即可~了解原理就好啰~ ^_^
while [ condition ] do 程序段落 done这种方式中, while 是『当....时』,所以,这种方式说的是『当 condition 条件成立时,就进行循环,直到 condition 的条件不成立才停止』的意思。
until [ condition ] do 程序段落 done这种方式恰恰与 while 相反,它说的是『当 condition 条件成立时,就终止循环, 否则就持续进行循环的程序段。』是否刚好相反啊~我们以 while 来做个简单的练习好了。 假设我要让用户输入 yes 或者是 YES 才结束程序的运行,否则就一直进行告知用户输入字符串。
[root@linux scripts]# vi sh12.sh #!/bin/bash # Program: # Use loop to try find your input. # History: # 2005/08/29 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH while [ "$yn" != "yes" ] && [ "$yn" != "YES" ] do read -p "Please input yes/YES to stop this program: " yn done上面这个例题的说明是『当 $yn 这个变量不是 "yes" 且 $yn 也不是 "YES" 时,才进行循环内的程序。』 而如果 $yn 是 "yes" 或 "YES" 时,就会离开循环啰~那如果使用 until 呢?呵呵有趣啰~ 他的条件会变成这样:
[root@linux scripts]# vi sh12-2.sh #!/bin/bash # Program: # Use loop to try find your input. # History: # 2005/08/29 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH until [ "$yn" == "yes" ] || [ "$yn" == "YES" ] do read -p "Please input yes/YES to stop this program: " yn done仔细比对一下这两个东西有啥不同喔! ^_^再来,如果我想要计算 1+2+3+....+100 这个数据呢? 利用循环啊~他是这样的:
[root@linux scripts]# vi sh13.sh #!/bin/bash # Program: # Try to use loop to calculate the result "1+2+3...+100" # History: # 2005/08/29 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH s=0 i=0 while [ "$i" != "100" ] do i=$(($i+1)) s=$(($s+$i)) done echo "The result of '1+2+3+...+100' is ==> $s"嘿嘿!当您运行了『 sh sh13.sh 』之后,就可以得到 5050 这个数据才对啊!这样瞭呼~ 那么让您自行做一下,如果想要让用户自行输入一个数字,让程序由 1+2+... 直到您输入的数字为止, 该如何撰写呢?应该很简单吧?!答案可以参考一下习题练习里面的一题喔!
for (( 初始值; 限制值; 运行步阶 )) do 程序段 done这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为:
[root@linux scripts]# vi sh14.sh #!/bin/bash # Program: # Try do calculate 1+2+....+100 # History: # 2005/08/29 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH s=0 for (( i=1; i<=100; i=i+1 )) do s=$(($s+$i)) done echo "The result of '1+2+3+...+100' is ==> $s"一样也是很简单吧!利用这个 for 则可以直接限制循环要进行几次呢!这么好用的东西难道只能在数值方面动作? 当然不是啦~我们还可以利用底下的方式来进行非数字方面的循环运作喔!
for var in con1 con2 con3 ... do 程序段 done以上面的例子来说,这个 $var 的变量内容在循环工作时:
[root@linux scripts]# vi sh15.sh #!/bin/bash # Program: # Using for .... loop to print 3 animal # History: # 2005/08/29 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH for animal in dog cat elephant do echo "There are ""$animal""s.... " done很简单是吧! ^_^。好了,那么如果我想要让用户输入某个目录, 然后我找出某目录内的文件名的权限呢?又该如何是好?呵呵!可以这样做啦~
[root@linux scripts]# vi sh16.sh #!/bin/bash # Program: # let user input a directory and find the whole file's permission. # History: # 2005/08/29 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH # 1. 先看看这个目录是否存在啊? read -p "Please input a directory: " dir if [ "$dir" == "" ] || [ ! -d "$dir" ]; then echo "The $dir is NOT exist in your system." exit 1 fi # 2. 开始测试文件啰~ filelist=`ls $dir` for filename in $filelist do perm="" test -r "$dir/$filename" && perm="$perm readable" test -w "$dir/$filename" && perm="$perm writable" test -x "$dir/$filename" && perm="$perm executable" echo "The file $dir/$filename's permission is $perm " done呵呵!很有趣的例子吧~利用这种方式,您可以很轻易的来处理一些文件的特性呢~ 我们循环就介绍到这里了~其他更多的应用,就得视您的需求来玩啰~。
[root@linux ~]# sh [-nvx] scripts.sh 参数: -n :不要运行 script,仅查找语法的问题; -v :再运行 sccript 前,先将 scripts 的内容输出到屏幕上; -x :将使用到的 script 内容显示到屏幕上,这是很有用的参数! 范例: 范例一:测试 sh16.sh 有无语法的问题? [root@linux ~]# sh -n sh16.sh # 若语法没有问题,则不会显示任何信息! 范例二:将 sh15.sh 的运行过程全部列出来~ [root@linux ~]# sh -x sh15.sh + PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/vbird/bin + export PATH + for animal in dog cat elephant + echo 'There are dogs.... ' There are dogs.... + for animal in dog cat elephant + echo 'There are cats.... ' There are cats.... + for animal in dog cat elephant + echo 'There are elephants.... ' There are elephants.... # 使用 -x 真的是追踪 script 的好方法,他可以将所有有运行的程序段在运行前列出来, # 如果是程序段落,则输出时,最前面会加上 + 字号,表示他是代码而已, # 实际的输出则与 standard output 有关啊~如上所示。在上面的范例二当中,我们可以通过这个简单的参数 -x 来达成 debug 的目的,这可是一个不可多得的参数, 通常如果您运行 script 却发生问题时,利用这个 -x 参数,就可以知道问题是发生在哪一行上面了!
# description: The portmapper manages RPC connections, which are used by \ # protocols such as NFS and NIS. The portmap server must be \ # running on machines which act as servers for protocols which \ # make use of the RPC mechanism. # processname: portmap简单的说,他是被用在 NFS 与 NIS 上面的一个启动 RPC 的 script , 然后我们再利用 http://www.google.com.tw 去搜索一下 NFS, NIS 与 RPC , 立刻就能够知道这个 script 的功能啰~所以,下次您发现不明的 script 时, 如果是系统提供的,那么利用这个检查的方式,一定可以约略了解的啦! 加油的啰~ ^_^