shell script的判断,函数,循环,追踪与debug

本文详细介绍了 Bash 脚本的基础知识和实战技巧,包括条件判断、循环控制、函数定义等内容,通过实例演示了如何创建高效实用的 Bash 脚本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在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)
-gtn1 大於 n2 (greater than)
-ltn1 小於 n2 (less than)
-gen1 大於等於 n2 (greater than or equal)
-len1 小於等於 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 来帮我们写几个简单的例子。首先,判断一下,让使用者输入一个档名,我们判断:

  1. 这个文件是否存在,若不存在则给予一个『Filename does not exist』的信息,并中断程序;
  2. 若这个文件存在,则判断他是个文件或目录,结果输出『Filename is regular file』或『Filename is directory』
  3. 判断一下,运行者的身份对这个文件或目录所拥有的权限,并输出权限数据!

你可以先自行创作看看,然后再跟底下的结果讨论讨论。注意利用 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 的情况中就是了。好,那我们也使用中括号的判断来做一个小案例好了,案例配置如下:

  1. 当运行一个程序的时候,这个程序会让使用者选择 Y 或 N ,
  2. 如果使用者输入 Y 或 y 时,就显示『 OK, continue 』
  3. 如果使用者输入 n 或 N 时,就显示『 Oh, interrupt !』
  4. 如果不是 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 (或) 连结两个判断喔!很有趣吧!利用这个字串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢!接下来,我们再来谈一些其他有的没有的东西吧!

      执行script命令的后面是否可以加一些参数呢?当然可以,下面我们会进行详细的讲解。

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 的变量,为何需要进行两次比对呢?此时,多重条件判断就能够来测试测试罗!

  • 多重、复杂条件判断式

在同一个数据的判断中,如果该数据需要进行多种不同的判断时,应该怎么作?举例来说,上面的sh06.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 喔!

利用 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 的变量内容在回圈工作时:

  1. 第一次回圈时, $var 的内容为 con1 ;
  2. 第二次回圈时, $var 的内容为 con2 ;
  3. 第三次回圈时, $var 的内容为 con3 ;
  4. ....

我们可以做个简单的练习。假设我有三种动物,分别是 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
done
for...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 则可以直接限制回圈要进行几次呢!

shell script 的追踪与 debug

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值