在bash下如果执行单一指令的话即可在bash下直接输入,但是如果想判断输入的文件名是否存在呢?那么肯定要进行一系列的指令,这时候便可以敲一个script(脚本),其实脚本就是去执行一系列的指令从而达到需求,脚本可以和用户进行交互,例如读取用户的输入。
利用 test 命令的测试功能
当我要检测系统上面某些文件或者是相关的属性时,利用 test 这个命令来工作真是好用得不得了,举例来说,我要检查 /dmtsai 是否存在时,使用:
[root@www ~]# test -e /dmtsai
|
运行结果并不会显示任何信息,但最后我们可以透过 $? 或 && 及 || 来展现整个结果呢!例如我们在将上面的例子改写成这样:
[root@www ~]# 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 来帮我们写几个简单的例子。首先,判断一下,让使用者输入一个档名,我们判断:
- 这个文件是否存在,若不存在则给予一个『Filename does not exist』的信息,并中断程序;
- 若这个文件存在,则判断他是个文件或目录,结果输出『Filename is regular file』或『Filename is directory』
- 判断一下,运行者的身份对这个文件或目录所拥有的权限,并输出权限数据!
你可以先自行创作看看,然后再跟底下的结果讨论讨论。注意利用 test 与 && 还有 || 等标志!
[root@www scripts]# vi sh05.sh
#!/bin/bash
# Program:
# User input a filename, program will check the flowing:
# 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 "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 are : $perm"
|
如果你运行这个脚本后,他会依据你输入的档名来进行检查喔!先看是否存在,再看为文件或目录类型,最后判断权限。但是你必须要注意的是,由於 root 在很多权限的限制上面都是无效的,所以使用 root 运行这个脚本时,常常会发现与 ls -l 观察到的结果并不相同!所以,建议使用一般使用者来运行这个脚本试看看。不过你必须要使用 root 的身份先将这个脚本搬移给使用者就是了,不然一般使用者无法进入 /root 目录的。很有趣的例子吧!你可以自行再以其他的案例来撰写一下可用的功能呢!
利用判断符号 [ ]
除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ] 』(就是中括号啦) 来进行数据的判断呢!举例来说,如果我想要知道 $HOME 这个变量是否为空的,可以这样做:
[root@www ~]# [ -z "$HOME" ] ; echo $?
|
使用中括号必须要特别注意,因为中括号用在很多地方,包括万用字节与正规表示法等等,所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空白字节来分隔喔!假设我空白键使用『□』符号来表示,那么,在这些地方你都需要有空白键:
[ "$HOME" == "$MAIL" ] [□"$HOME"□==□"$MAIL"□] ↑ ↑ ↑ ↑ |
Tips: 你会发现鸟哥在上面的判断式当中使用了两个等号『 == 』。其实在 bash 当中使用一个等号与两个等号的结果是一样的!不过在一般惯用程序的写法中,一个等号代表『变量的配置』,两个等号则是代表『逻辑判断 (是否之意)』。由於我们在中括号内重点在於『判断』而非『配置变量』,因此鸟哥建议您还是使用两个等号较佳! |
上面的例子在说明,两个字串 $HOME 与 $MAIL 是否相同的意思,相当於 test $HOME = $MAIL 的意思啦!而如果没有空白分隔,例如 [$HOME==$MAIL] 时,我们的 bash 就会显示错误信息了!这可要很注意啊!所以说,你最好要注意:
- 在中括号 [] 内的每个组件都需要有空白键来分隔;
- 在中括号内的变量,最好都以双引号括号起来;
- 在中括号内的常数,最好都以单或双引号括号起来。
为什么要这么麻烦啊?直接举例来说,假如我配置了 name="VBird Tsai" ,然后这样判定:
[root@www ~]# name="VBird Tsai" [root@www ~]# [ $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 』
[root@www scripts]# vi sh06.sh #!/bin/bash # Program: # This program shows 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 your choice is" && exit 0 |
由於输入正确 (Yes) 的方法有大小写之分,不论输入大写 Y 或小写 y 都是可以的,此时判断式内就得要有两个判断才行!由於是任何一个成立即可 (大小或小写的 y) ,所以这里使用 -o (或) 连结两个判断喔!很有趣吧!利用这个字串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢!接下来,我们再来谈一些其他有的没有的东西吧!
Shell script 的默认变量($0, $1...)
我们知道命令可以带有选项与参数,例如 ls -la 可以察看包含隐藏档的所有属性与权限。那么 shell script 能不能在脚本档名后面带有参数呢?很有趣喔!举例来说,如果你想要重新启动系统登录档的功能,可以这样做:
[root@www ~]# file /etc/init.d/syslog /etc/init.d/syslog: Bourne-Again shell script text executable # 使用 file 来查询后,系统告知这个文件是个 bash 的可运行 script 喔! [root@www ~]# /etc/init.d/syslog restart |
restart 是重新启动的意思,上面的命令可以『重新启动 /etc/init.d/syslog 这支程序』的意思!唔!那么如果你在 /etc/init.d/syslog 后面加上 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 则告知使用者参数数量太少
- 全部的参数内容为何?
- 第一个参数为何?
- 第二个参数为何
[root@www scripts]# vi sh07.sh #!/bin/bash # Program: # Program shows the script name, parameters... # History: # 2009/02/17 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" |
运行结果如下:
[root@www scripts]# sh sh07.sh theone haha quot The script name is ==> sh07.sh <==档名 Total parameter number is ==> 3 <==果然有三个参数 Your whole parameter is ==> 'theone haha quot' <==参数的内容全部 The 1st parameter ==> theone <==第一个参数 The 2nd parameter ==> haha <==第二个参数
- shift:造成参数变量号码偏移
除此之外,脚本后面所接的变量是否能够进行偏移 (shift) 呢?什么是偏移啊?我们直接以底下的范例来说明好了,用范例说明比较好解释!我们将 sh07.sh 的内容稍作变化一下,用来显示每次偏移后参数的变化情况:
[root@www scripts]# vi sh08.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 ==> '$@'" |
这玩意的运行成果如下:
[root@www scripts]# sh sh08.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 的功能了吗?
上面这8个例子都很简单吧?几乎都是利用 bash 的相关功能而已~不难啦~底下我们就要使用条件判断式来进行一些分别功能的配置了,好好瞧一瞧先~
- 单层、简单条件判断式
如果你只有一个判断式要进行,那么我们可以简单的这样看:
if [ 条件判断式 ]; then 当条件判断式成立时,可以进行的命令工作内容; fi <==将 if 反过来写,就成为 fi 啦!结束 if 之意! |
至於条件判断式的判断方法,与前一小节的介绍相同啊!较特别的是,如果我有多个条件要判别时,除了 sh06.sh 那个案例所写的,也就是『将多个条件写入一个中括号内的情况』之外,我还可以有多个中括号来隔开喔!而括号与括号之间,则以 && 或 || 来隔开,他们的意义是:
- && 代表 AND ;
- || 代表 or ;
所以,在使用中括号的判断式中, && 及 || 就与命令下达的状态不同了。举例来说,sh06.sh 里面的判断式可以这样修改:
[ "$yn" == "Y" -o "$yn" == "y" ]
上式可替换为
[ "$yn" == "Y" ] || [ "$yn" == "y" ]
之所以这样改,很多人是习惯问题!很多人则是喜欢一个中括号仅有一个判别式的原因。好了,现在我们来将 sh06.sh 这个脚本修改成为 if ... then 的样式来看看:
[root@www scripts]# cp sh06.sh sh06-2.sh <==用改的比较快! [root@www scripts]# vi sh06-2.sh #!/bin/bash # Program: # This program shows 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 your choice is" && exit 0 |
- 多重、复杂条件判断式
在同一个数据的判断中,如果该数据需要进行多种不同的判断时,应该怎么作?举例来说,上面的sh06.sh 脚本中,我们只要进行一次 $yn 的判断就好 (仅进行一次 if ),不想要作多次 if 的判断。此时你就得要知道底下的语法了:
# 一个条件判断,分成功进行与失败进行 (else) if [ 条件判断式 ]; then 当条件判断式成立时,可以进行的命令工作内容; else 当条件判断式不成立时,可以进行的命令工作内容; fi |
如果考虑更复杂的情况,则可以使用这个语法:
# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况运行 if [ 条件判断式一 ]; then 当条件判断式一成立时,可以进行的命令工作内容; elif [ 条件判断式二 ]; then 当条件判断式二成立时,可以进行的命令工作内容; else 当条件判断式一与二均不成立时,可以进行的命令工作内容; fi |
利用 case ..... esac 判断
上个小节提到的『 if .... then .... fi 』对於变量的判断是以『比对』的方式来分辨的,如果符合状态就进行某些行为,并且透过较多层次 (就是 elif ...) 的方式来进行多个变量的程序码撰写,譬如 sh09.sh 那个小程序,就是用这样的方式来撰写的罗。好,那么万一我有多个既定的变量内容,例如 sh09.sh 当中,我所需要的变量就是 "hello" 及空字串两个,那么我只要针对这两个变量来配置状况就好了,对吧?那么可以使用什么方式来设计呢?呵呵~就用 case ... in .... esac 吧~,他的语法如下:
case $变量名称 in <==关键字为 case ,还有变量前有钱字号 "第一个变量内容") <==每个变量内容建议用双引号括起来,关键字则为小括号 ) 程序段 ;; <==每个类别结尾使用两个连续的分号来处理! "第二个变量内容") 程序段 ;; *) <==最后一个变量内容都会用 * 来代表所有其他值 不包含第一个变量内容与第二个变量内容的其他程序运行段 exit 1 ;; esac <==最终的 case 结尾!『反过来写』思考一下! |
要注意的是,这个语法以 case (实际案例之意) 为开头,结尾自然就是将 case 的英文反过来写!就成为 esac 罗!不会很难背啦!另外,每一个变量内容的程序段最后都需要两个分号 (;;) 来代表该程序段落的结束,这挺重要的喔!至於为何需要有 * 这个变量内容在最后呢?这是因为,如果使用者不是输入变量内容一或二时,我们可以告知使用者相关的资讯啊!废话少说,我们拿 sh09.sh 的案例来修改一下,他应该会变成这样喔:
[root@www scripts]# vi sh09-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}" ;; *) # 其实就相当於万用字节,0~无穷多个任意字节之意! echo "Usage $0 {hello}" ;; esac
什么是『函数 (function)』功能啊?简单的说,其实,函数可以在 shell script 当中做出一个类似自订运行命令的东西,最大的功能是,可以简化我们很多的程序码~举例来说,上面的 sh12.sh 当中,每个输入结果 one, two, three 其实输出的内容都一样啊~那么我就可以使用 function 来简化了! function 的语法是这样的:
function fname() { 程序段 } |
那个 fname 就是我们的自订的运行命令名称~而程序段就是我们要他运行的内容了。要注意的是,因为 shell script 的运行方式是由上而下,由左而右,因此在 shell script 当中的 function 的配置一定要在程序的最前面,这样才能够在运行时被找到可用的程序段喔!好~我们将 sh12.sh 改写一下,自订一个名为 printit 的函数来使用喔:
[root@www scripts]# vi sh12-2.sh #!/bin/bash # Program: # Use function to repeat information. # 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 " # 加上 -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 可就重要的多罗~不但可以简化程序码,而且可以做成类似『模块』的玩意儿,真的很棒啦!
在上面的例子当中,如果你输入『 sh sh12-3.sh one 』就会出现『 Your choice is 1 』的字样~为什么是 1 呢?因为在程序段落当中,我们是写了『 printit 1 』那个 1 就会成为 function 当中的 $1 喔~这样是否理解呢? function 本身其实比较困难一点,如果你还想要进行其他的撰写的话。不过,我们仅是想要更加了解 shell script 而已,所以,这里看看即可~了解原理就好罗~ ^_^
while do done, until do done (不定回圈)
一般来说,不定回圈最常见的就是底下这两种状态了:
while [ condition ] <==中括号内的状态就是判断式 do <==do 是回圈的开始! 程序段落 done <==done 是回圈的结束 |
while 的中文是『当....时』,所以,这种方式说的是『当 condition 条件成立时,就进行回圈,直到 condition 的条件不成立才停止』的意思。还有另外一种不定回圈的方式:
until [ condition ] do 程序段落 done |
这种方式恰恰与 while 相反,它说的是『当 condition 条件成立时,就终止回圈,否则就持续进行回圈的程序段。』是否刚好相反啊~我们以 while 来做个简单的练习好了。假设我要让使用者输入 yes 或者是 YES 才结束程序的运行,否则就一直进行告知使用者输入字串。
[root@www scripts]# vi sh13.sh #!/bin/bash # Program: # Repeat question until user input correct answer. # 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" -a "$yn" != "YES" ] do read -p "Please input yes/YES to stop this program: " yn done echo "OK! you input the correct answer." |
上面这个例题的说明是『当 $yn 这个变量不是 "yes" 且 $yn 也不是 "YES" 时,才进行回圈内的程序。』而如果 $yn 是 "yes" 或 "YES" 时,就会离开回圈罗~那如果使用 until 呢?呵呵有趣罗~他的条件会变成这样:
[root@www scripts]# vi sh13-2.sh #!/bin/bash # Program: # Repeat question until user input correct answer. # 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" -o "$yn" == "YES" ] do read -p "Please input yes/YES to stop this program: " yn done echo "OK! you input the correct answer."
for...do...done (固定回圈)
相对於 while, until 的回圈方式是必须要『符合某个条件』的状态, for 这种语法,则是『已经知道要进行几次回圈』的状态!他的语法是:
for var in con1 con2 con3 ... do 程序段 done |
以上面的例子来说,这个 $var 的变量内容在回圈工作时:
- 第一次回圈时, $var 的内容为 con1 ;
- 第二次回圈时, $var 的内容为 con2 ;
- 第三次回圈时, $var 的内容为 con3 ;
- ....
我们可以做个简单的练习。假设我有三种动物,分别是 dog, cat, elephant 三种,我想每一行都输出这样:『There are dogs...』之类的字样,则可以:
[root@www scripts]# vi sh15.sh #!/bin/bash # Program: # Using for .... loop to print 3 animals # 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 |
等你运行之后就能够发现这个程序运行的情况啦!让我们想像另外一种状况,由於系统上面的各种帐号都是写在 /etc/passwd 内的第一个栏位,你能不能透过管线命令的 cut捉出单纯的帐号名称后,以 id 及 finger 分别检查使用者的识别码与特殊参数呢?由於不同的 Linux 系统上面的帐号都不一样!此时实际去捉 /etc/passwd并使用回圈处理,就是一个可行的方案了!程序可以如下:
[root@www scripts]# vi sh16.sh #!/bin/bash # Program # Use id, finger command to check system account's information. # History # 2009/02/18 VBird first release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH users=$(cut -d ':' -f1 /etc/passwd) # 撷取帐号名称 for username in $users # 开始回圈进行! do id $username finger $username donefor...do...done 的数值处理
除了上述的方法之外,for 回圈还有另外一种写法!语法如下:
for (( 初始值; 限制值; 运行步阶 )) do 程序段 done |
这种语法适合於数值方式的运算当中,在 for 后面的括号内的三串内容意义为:
- 初始值:某个变量在回圈当中的起始值,直接以类似 i=1 配置好;
- 限制值:当变量的值在这个限制值的范围内,就继续进行回圈。例如 i<=100;
- 运行步阶:每作一次回圈时,变量的变化量。例如 i=i+1。
值得注意的是,在『运行步阶』的配置上,如果每次添加 1 ,则可以使用类似『i++』的方式,亦即是 i 每次回圈都会添加一的意思。好,我们以这种方式来进行 1 累加到使用者输入的回圈吧!
[root@www scripts]# vi sh19.sh #!/bin/bash # Program: # Try do calculate 1+2+....+${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 read -p "Please input a number, I will count for 1+2+...+your_input: " nu s=0 for (( i=1; i<=$nu; i=i+1 )) do s=$(($s+$i)) done echo "The result of '1+2+3+...+$nu' is ==> $s" |
一样也是很简单吧!利用这个 for 则可以直接限制回圈要进行几次呢!
scripts 在运行之前,最怕的就是出现语法错误的问题了!那么我们如何 debug 呢?有没有办法不需要透过直接运行该 scripts 就可以来判断是否有问题呢?呵呵!当然是有的!我们就直接以 bash 的相关参数来进行判断吧!
[root@www ~]# sh [-nvx] scripts.sh 选项与参数: -n :不要运行 script,仅查询语法的问题; -v :再运行 sccript 前,先将 scripts 的内容输出到萤幕上; -x :将使用到的 script 内容显示到萤幕上,这是很有用的参数! 范例一:测试 sh16.sh 有无语法的问题? [root@www ~]# sh -n sh16.sh # 若语法没有问题,则不会显示任何资讯! 范例二:将 sh15.sh 的运行过程全部列出来~ [root@www ~]# sh -x sh15.sh + PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/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....重点回顾
- shell script 是利用 shell 的功能所写的一个『程序 (program)』,这个程序是使用纯文字档,将一些 shell 的语法与命令(含外部命令)写在里面, 搭配正规表示法、管线命令与数据流重导向等功能,以达到我们所想要的处理目的
- shell script 用在系统管理上面是很好的一项工具,但是用在处理大量数值运算上, 就不够好了,因为 Shell scripts 的速度较慢,且使用的 CPU 资源较多,造成主机资源的分配不良。
- 在 Shell script 的文件中,命令的运行是从上而下、从左而右的分析与运行;
- shell script 的运行,至少需要有 r 的权限,若需要直接命令下达,则需要拥有 r 与 x 的权限;
- 良好的程序撰写习惯中,第一行要宣告 shell (#!/bin/bash) ,第二行以后则宣告程序用途、版本、作者等
- 对谈式脚本可用 read 命令达成;
- 要创建每次运行脚本都有不同结果的数据,可使用 date 命令利用日期达成;
- script 的运行若以 source 来运行时,代表在父程序的 bash 内运行之意!
- 若需要进行判断式,可使用 test 或中括号 ( [] ) 来处理;
- 在 script 内,$0, $1, $2..., $@ 是有特殊意义的!
- 条件判断式可使用 if...then 来判断,若是固定变量内容的情况下,可使用 case $var in ... esac 来处理
- 回圈主要分为不定回圈 (while, until) 以及固定回圈 (for) ,配合 do, done 来达成所需任务!
- 我们可使用 sh -x script.sh 来进行程序的 debug