http://blog.chinaunix.net/space.php?uid=9809038&do=blog&cuid=62903php
经典入门材料!node
学习 Shell Scripts
若是您真的很想要走信息这条路,而且想要好好的管理好属于您的主机,那么,别说鸟哥不告诉您, Shell Scripts 真的是必需要学习的一项课题呢!基本上, shell script 有点像是早期的批次档, 亦便是将一些指令汇整起来一次执行,可是 Shell script 拥有更强大的功能,那就是, 他能够进行相似程序 (program) 的撰写,而且,不须要通过编译 (compiler) 就可以执行, 真的很方便。加上,咱们可透过 shell script 来简化咱们平常的工做管理, 并且,整个 Linux 环境中,一些服务 (services) 的启动都是透过 shell script 的, 若是您对于 script 不了解,嘿嘿!发生问题时,可真是会求助无门喔! 因此,好好的学一学他吧!
1. 什么是 Shell Script
1.1 干吗学习 shell scripts?
1.2 第一支 script 的撰写与执行
1.3 撰写 shell script 的良好习惯创建
2. 简单的 shell script 练习:
3. 善用判断式:
3.1 利用 test 指令的测试功能
3.2 利用判断符号 [ ]
3.3 Shell script 的预设变数($0, $1...)
4. 条件判断式:
4.1 利用 if .... then
4.2 利用 case ..... esac 判断
4.3 利用 function 功能
5. 循环 (loop)
5.1 while....do....done, until....do....done
5.2 for...do...done
6. shell script 的追踪与 debug
7. 本章习题练习
--------------------------------------------------------------------------------
什么是 Shell scripts ?
这个有趣的问题赶忙来回答看看,什么是 shell script 呢? shell 咱们在 认识 bash 当中已经提过了,那是一个文字接口底下让咱们与系统沟通的一个工具接口,那么 script 是啥? 字面上的意义, script 是『脚本、剧本』的意思。整句话是说, shell script 是针对 shell 所写的『剧本!』 什么东西啊?呵呵!其实, shell script 是利用 shell 的功能所写的一个『程序 (program)』,这个程序是使用纯文字文件,将一些 shell 的语法与指令写在里面, 搭配正规表示法、管线命令与数据流重导向等功能,以达到咱们所想要的处理目的。
因此,简单的说, shell script 就像是早期 DOS 年代的批次檔 (.bat) ,最简单的功能就是将许多指令汇整写在一块儿, 让使用者很轻易的就可以 one touch (执行一个档案 "shell script" ,就可以一次执行多个指令), 而, shell script 更提供数组、循环、条件与逻辑判断等重要功能,让使用者也能够直接以 shell 来撰写程序,而没必要使用相似 C 程序语言等传统程序撰写的语法呢!
那,这么说您能够了解了吗?是的! shell script 能够简单的被当作是批次档, 也能够被说成是一个程序语言,且这个程序语言因为都是利用 shell 与相关工具指令, 因此不须要编译便可执行,且拥有不错的除错 (debug) 工具,因此,他能够帮助系统管理员快速的管理好主机。
--------------------------------------------------------------------------------
干吗学习 shell scripts?
这是个好问题,我又干吗必定要学 shell script ?我又不是信息人,没有写程序的概念, 那我干吗还要学 shell script 呢?不要学可不能够啊?呵呵~若是 Linux 对您而言, 您只是想要『会用』而已,那么,不须要学 shell script 也还无所谓,这部分先给他跳过去, 等到有空的时候,再来好好的瞧一瞧。可是,若是您是真的想要玩清楚 Linux 的前因后果, 那么 shell script 就不可不知,为何呢?由于:
自动化管理的重要依据:
不用鸟哥说您也知道,管理一部主机真不是件简单的事情,天天要进行的任务就有: 查询登陆档、追踪流量、监控使用者使用主机状态、主机各项硬设备状态、 主机软件更新查询、更不要说得应付其它使用者的忽然要求了。而这些工做, 您想要自行手动处理,仍是写个简单的程序来帮您每日自动处理分析,如有问题才通知您呢? 固然是让系统自动工做比较好,对吧!呵呵~这就得要良好的 shell script 来帮忙的啦!
追踪与管理系统的重要工做:
虽然咱们尚未提到服务启动的方法,不过,这里能够先提一下,咱们 Linux 系统的服务 ( services ) 启动的接口,在 /etc/init.d/ 这个目录下,全部的档案都是 scripts ; 另外,包括开机 (booting) 过程也都是利用 shell script 来帮忙搜寻系统的相关设定数据, 而后再代入各个服务的设定参数啊!举例来讲,若是咱们想要从新启动系统登陆文件, 可使用:『/etc/init.d/syslogd restart』,那个 syslogd 档案就是 script 啦! 另外,我曾经在某一代的 FC 上面发现,启动 MySQL 这个数据库服务时,确实是能够启动的, 可是屏幕上却总是出现『failure』,后来才发现,原来是启动 MySQL 那个 script 会主动的以『空的密码』去尝试登入 MySQL ,但我修改过 MySQL 的密码啰~固然就登入失败~ 后来改了改 script ,就略去这个问题啦!如此说来, script 确实是须要学习的啊!
简单入侵侦测功能:
当咱们的系统有异状时,大多会将这些异状记录在系统记录器,也就是咱们常提到的『系统登陆文件』, 那么咱们能够在固定的几分钟内主动的去分析系统登陆文件,若察觉有问题,就马上通报管理员, 或者是马上增强防火墙的设定规则,如此一来,您的主机可就可以达到『自我保护』的聪明学习功能啦~ 举例来讲,咱们能够经过 shell script 去分析『当该封包尝试几回仍是联机失败以后,就予以抵挡住该 IP』之类的举动,例如鸟哥写过一个关于抵挡砍站软件的 shell script , 就是用这个想法去达成的呢!
连续指令单一化:
其实,对于新手而言, script 最简单的功能就是:『汇整一些在 command line 下达的连续指令,将他写入 scripts 当中,而由直接执行 scripts 来启动一连串的 command line 指令输出入!』例如: 防火墙连续规则 ( iptables ),开机加载程序的项目 ( 就是在 /etc/rc.d/rc.local 里头的数据 ) ,等等都是类似的功能啦! 其实,说穿了,若是不考虑 program 的部分,那么 scripts 也能够想成,仅是帮咱们把一大串的指令汇整在一个档案里面, 而直接执行该档案就能够执行那一串又臭又长的指令段!就是这么简单啦!
简易的数据处理:
由前一章 正规表示法 的 awk 程序说明中, 您能够发现, awk 能够用来处理简单的数据数据呢!例如薪资单的处理啊等等的。 shell script 的功能更强大,例如鸟哥曾经用 shell script 直接处理数据数据的比对啊, 文字数据的处理啊等等的,撰写方便,速度又快(由于在 Linux 效能较佳), 真的是很不错用的啦!
跨平台支持与学习历程较短:
几乎全部的 Unix Like 上面均可以跑 shell script ,连 MS Windows 系列也有相关的仿真器能够用, 此外, shell script 的语法是至关亲和的,看都看的懂得文字,而不是机器码, 很容易学习~这些都是您能够加以考虑的学习点啊!
上面这些都是您考虑学习 shell script 的特色~此外, shell script 还能够简单的以 vi 来直接编写,实在是很方便的好东西!因此,仍是建议您学习一下啦。
不过,虽然 shell script 号称是程序 (program) ,但实际上, shell script 处理数据的速度上是不太够的。由于 shell script 用的是外部的指令与 bash shell 的一些预设工具,因此,他经常会去呼叫外部的函式库,所以,运算速度上面固然比不上传统的程序语言。 因此啰, shell script 用在系统管理上面是很好的一项工具,可是用在处理大量数值运算上, 就不够好了~并且还很麻烦,由于:Shell scripts 的速度较慢, 且使用的 CPU 资源较多,形成主机资源的分配不良。还好, 咱们确实不多看到利用 shell script 在进行大量数据运算的,因此,没必要担忧的啦!
--------------------------------------------------------------------------------
第一支 script 的撰写与执行
如同前面讲到的, shell script 其实就是纯文字文件 (ASCII) ,咱们能够编辑这个档案, 而后让这个档案来帮咱们一次执行多个指令,或者是利用一些运算与逻辑判断来帮咱们达成某些功能。 因此啦,要编辑这个档案的内容时,固然就须要具有有 bash shell 指令下达的相关认识。 咱们说过,要下达指令须要注意的事项在 bash 章节内已经提过, 在 shell script 的撰写一样须要用到这些注意事项的:
如同前面 bash command 提到的,指令与参数间的多个空白会被忽略掉;
而空白行也将被忽略掉!,而且 [tab] 也是不会被理会的!
若是读取到一个 Enter 符号 ( CR )),就尝试开始执行该行命令;
至于若是一行的内容太多,则可使用 [Enter] 来延伸至下一行;
此外,使用最多的 # 可作为批注!任何加在 # 后面的字,将所有被视为批注文字而被忽略!
如此一来,咱们在 script 内所撰写的程序,就会被一行一行的执行。好了,那么这个程序假设文件名是 shell.sh 好了,如何执行这个档案?很简单,能够有底下几个方法:
将 shell.sh 加上可读与执行 (rx) 的权限,而后就可以以 ./shell.sh 来执行了;
直接以 sh shell.sh 的方式来直接执行便可。
反正重点就是要让那个 shell.sh 内的指令能够被执行的意思啦!咦!那我为什么须要使用 ./shell.sh 来下达指令? 还记得咱们在 bash 里面一直强调的,指令是否可以被执行与 PATH 这个环境变量有关, 因此,要执行『目前这个目录下的某个档案』就须要加上 ./ 这个目录啦!另外,其实您也能够将 shell.sh 放在您家目录下的 ~/bin 这个目录中,而后利用 PATH="$PATH":~/bin 的设定, 嘿嘿,就可以直接执行您的 script 啰~ ^_^
那,为什么 sh shell.sh 也能够执行呢?这是由于 /bin/sh 其实就是 /bin/bash , 使用 sh shell.sh 亦即告诉系统,我想要直接以 bash 的功能来执行 shell.sh 这个档案内的相关指令的意思。 而咱们也能够利用 sh 的参数,如 -n 及 -x 来检查与追踪 shell.sh 的语法是否正确呢! ^_^
--------------------------------------------------------------------------------
撰写第一支 script
不管是那个门派,要学武功要从扫地作起,那么要学程序呢?呵呵,确定是由『秀出 Hello World!』 这个字眼开始的!OK!那么鸟哥就先写一支 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 "
exit 0
在咱们这个章节当中,请将全部的撰写的 script 放置到您家目录的 ~/scripts 这个目录内, 比较好管理啦!上面的写法当中,我主要将整个程序的撰写分红数段,大体是这样:
第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:
由于咱们使用的是 bash ,因此,必需要以『 #!/bin/bash 』来宣告这个档案内的语法使用 bash 的语法!那么当这个程序被执行时,他就可以加载 bash 的相关环境设定档, 而且执行 bash 来使咱们底下的指令可以执行!这很重要的!(在不少情况中,若是没有设定好这一行, 那么该程序极可能会没法执行,由于系统可能没法判断该程序须要使用什么 shell 来执行啊!)
程序内容的宣告:
整个 script 当中,除了第一行的 #! 是用来宣告 shell 的以外,其它的 # 都是『批注』用途! 因此上面的程序当中,第二行如下就是用来讲明整个程序的状态。通常来讲, 建议您必定要养成说明该 script 的:1. 内容与功能; 2. 版本信息; 3. 做者与联络方式; 4. 建檔日期;5. 历史纪录 等等。这将有助于将来程序的改写与 debug 呢!
主要环境变量的宣告:
建议务必要将一些重要的环境变量设定好,鸟哥我的认为, PATH 是当中最重要的! 如此一来,则可以让咱们这支程序在进行时,能够直接下达指令, 而没必要写绝对路径呢!比较好啦!
主要程序部分
就将主要的程序写好便可!在这个例子当中,就是 echo 那一行啦!
执行成果告知
是否记得咱们在 bash 里面要讨论一个指令的执行成功与否,可使用 $? 这个变量来观察~ 那么咱们也能够利用 exit 这个指令来让程序中断,而且回传一个数值给系统。 在咱们这个例子当中,我使用 exit 0 ,这表明离开 script ,而且回传一个 0 给系统, 因此我执行完这个 script 后,若接着下达 echo $? 则可获得 0 的值喔! 更聪明的读者应该也知道了,呵呵!利用这个 exit n 的功能,咱们还能够自订错误讯息, 让这支程序变得更加的 smart 呢!
接下来执行看看结果是怎样吧?
[root@linux scripts]# sh sh01.sh
Hello World !
您会看到屏幕是这样,并且应该还会听到『咚』的一声,为何呢?还记得前一章提到的 printf 吧?用 echo 接着那些特殊的按键也能够发生一样的事情~ 不过, echo 必需要加上 -e 的参数才行! 呵呵!在您写完这个小 script 以后,您就能够大声的说:『我也会写程序了』!哈哈! 很简单有趣吧~ ^_^
另外,你也能够利用:『chmod a+x sh01.sh; ./sh01.sh』来执行这个 script 的呢!
--------------------------------------------------------------------------------
撰写 shell script 的良好习惯创建
一个良好习惯的养成是很重要的~你们在刚开始撰写程序的时候,最容易忽略这部分, 认为程序写出来就行了,其它的不重要。其实,若是程序的说明可以更清楚, 那么对您本身是有很大的帮助的。
举例来讲,鸟哥本身为了本身的需求,曾经撰写了很多的 script 来帮我进行主机 IP 的侦测啊、 登陆档分析与管理啊、自动上传下载重要设定档啊等等的,不过,早期就是由于太懒了, 管理的主机又太多了,经常同一个程序在不一样的主机上面进行更改,到最后,到底哪一支才是最新的都记不起来, 并且,重点是,我究竟是改了哪里??为何作那样的修改?都忘的一乾二净~真要命~
因此,后来鸟哥在写程序的时候,一般会比较仔细的将程序的设计过程给他记录下来, 并且还会记录一些历史纪录,如此一来,好多了~ 至少很容易知道我修改了哪些数据,以及程序修改的理念与逻辑概念等等, 在维护上面是轻松不少不少的喔!
另外,在一些环境的设定上面,毕竟每一个人的环境都不相同,为了取得较佳的执行环境, 我都会自行先定义好一些必定会被用到的环境变量,例如 PATH 这个玩意儿! 这样比较好啦~因此说,建议您必定要养成良好的 script 撰写习惯, 在每一个 script 的文件头处记录好:
script 的功能;
script 的版本信息;
script 的做者与联络方式;
script 的版权宣告方式;
script 的 History (历史纪录);
script 内较特殊的指令,使用绝对路径的方式来下达;
script 运做时须要的环境变量预先宣告与设定。
--------------------------------------------------------------------------------
简单的 shell script 练习
在第一支 shell script 撰写完毕以后,相信您应该具备基本的撰写功力了。 接下来,在开始更深刻的程序概念以前,咱们先来玩一些比较有趣的简单的小范例好了。 底下的范例中,达成结果的方式至关的多,建议您先自行撰写看看,写完以后再与鸟哥写的内容比对, 这样才能更加深概念喔!好!不啰唆,咱们就一个一个来玩吧!
--------------------------------------------------------------------------------
变量内容由使用者决定
不少时候咱们须要使用者输入一些内容,好让程序能够顺利运做。 简单的来讲,你们应该都有安装过软件的经验,安装的时候,他不是会问您『要安装到那个目录去?』吗? 那个让使用者输入的数据的动做,就是让使用者输入变量内容啦。
你应该还记得在 bash 的时候,咱们有学到一个 read 指令吧?忘记的话,请自行回头去阅读一番。 如今,请你以 read 指令的用途,撰写一个 script ,他可让使用者输入:1 first name 与 2. last name, 最后而且在屏幕上显示:『Your full name is: 』的内容:
[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 " Your full name is: $firstname $lastname"
将上面这个 sh02.sh 执行一下,你就可以发现使用者本身输入的变量能够被取用的哩! 很不错吧!加油!
--------------------------------------------------------------------------------
利用 date 进行档案的创建
想象一个情况,若是我天天要进行备份,而备份的数据又不想被覆盖掉,也就是说, 我想要将天天备份的数据放在不一样的档案中。哇!这真困扰啊?难道要我天天去修改 script ? 不须要啊!由于天天的『日期』并不相同,因此我能够将档名取成相似: backup.20050802 , 不就能够天天一个不一样档名了吗?呵呵!确实如此。好了,接下来出个例子: 我想要创建三个空的档案,档名最开头由使用者输入决定,假设使用者输入 filename 好了, 那今天的日期是 2005/08/23 ,我想要之前天、昨天、今天的日期来创建这个档案,亦即 filename_20050821, filename_20050822, filename_20050823 ,该如何是好?
[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 吧! ^_^
--------------------------------------------------------------------------------
数值运算的方法
各位看官应该还记得,咱们可使用 declare 来定义变量的类型吧?! 这样才可以进行加减运算啊!惋惜的是, bash shell 里头预设仅支持到整数的数据。 OK!那咱们来玩玩看,若是咱们要使用者输入两个变量,而后将两个变量的内容相乘, 最后输出相乘的结果,那能够怎么作?
[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 they! "
read -p "first number: " firstnu
read -p "second number: " secnu
total=$(($firstnu*$secnu))
echo -e " The number $firstnu x $secnu is ==>; $total"
在数字的运算上,咱们可使用『 declare -i total=$firstnu*$secnu 』 也可使用上面的方式来进行!基本上,鸟哥比较建议使用这样的方式来进行运算:
var=$((运算内容))
不但容易记忆,并且也比较方便的多~将来您可使用这种方式来计算的呀!至于数值运算上的处理, 则有:+, -, *, /, %等等。 那个 % 是取余数啦~举例来讲, 13 对 3 取余数,结果是 13=4*3+1,因此余数是 1 啊!就是:
[root@linux scripts]# nu=$((13%3)); echo $nu
1
这样了解了吧?!多多学习与应用喔! ^_^
--------------------------------------------------------------------------------
善用判断式
在 bash 章节中,咱们提到过 $? 这个变量所表明的意义, 此外,也透过 && 及 || 来做为前一个指令是否可以成功进行的一个参考。 那么,若是我想要知道 /dmtsai 这个目录是否存在时,难道必定要使用 ls 来执行, 而后再以 $? 来判断执行成果吗?呵呵!固然不须要! 咱们能够透过『 test 』这个指令来侦测呢!
--------------------------------------------------------------------------------
利用 test 指令的测试功能
当我要检测系统上面某些档案或者是相关的属性时,利用 test 这个指令来工做, 真是好用得不得了,举例来讲,我要检查 /dmtsai 是否存在时,使用:
[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
OK!如今咱们就利用 test 来帮咱们写几个简单的例子。首先,判断一下, 让使用者输入一个档名,咱们判断:
这个档案是否存在,若不存在则给予一个『Filename does not exist』的讯息,并中断程序;
若这个档案存在,则判断他是个档案或目录,结果输出『Filename is regular file』或 『Filename is directory』
判断一下,执行者的身份对这个档案或目录所拥有的权限,并输出权限数据!
你能够先自行创做看看,而后再跟底下的结果讨论讨论。注意利用 test 与 && 还有 || 等标志!
[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. "
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"
颇有趣的例子吧!您能够自行再以其它的案例来撰写一下可用的功能呢!
--------------------------------------------------------------------------------
利用判断符号 [ ]
除了咱们很喜欢使用的 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 行加上批注 (#),就可让使用者输入参数啰~这样是否颇有趣啊?!
--------------------------------------------------------------------------------
利用 function 功能
什么是『函数 (function)』功能啊?简单的说,其实, 函数能够在 shell script 当中作出一个相似自订执行指令的东西,最大的功能是, 能够简化咱们不少的程序代码~举例来讲,上面的 sh11.sh 当中,每一个输入结果 one, two, three 其实输出的内容都同样啊~那么我就可使用 function 来简化了! function 的语法是这样的:
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
以上面的例子来讲,我作了一个函数名称为 printif ,因此,当我在后续的程序段里面, 只要执行 printit 的话,就表示个人 shell script 要去执行『 function printit .... 』 里面的那几个程序段落啰! 固然啰,上面这个例子举得太简单了,因此您不会以为 function 有什么好厉害的, 不过,若是某些程序代码一再地在 script 当中重复时,这个 function 可就重要的多啰~ 不但能够简化程序代码,并且能够作成相似『模块』的玩意儿,真的很棒啦!
另外, function 也是拥有内建变量的~他的内建变量与 shell script 很相似, 函数名称表明示 $0 ,然后续接的变量也是以 $1, $2... 来取代的~ 这里很容易搞错喔~由于『 function fname() { 程序段 } 』内的 $0, $1... 等等与 shell script 的 $0 是不一样的。以上面 sh11-2.sh 来讲,假如我下达:『 sh sh11-2.sh one 』 这表示在 shell script 内的 $1 为 "one" 这个字符串。可是在 printit() 内的 $1 则与这个 one 无关。 咱们将上面的例子再次的改写一下,让您更清楚!
[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 而已,因此,这里看看便可~了解原理就好啰~ ^_^
--------------------------------------------------------------------------------
循环 (loop)
除了 if...then...fi 这种条件判断式以外,循环多是程序当中最重要的一环了~ 循环能够不断的执行某个程序段落,直到使用者设定的条件达成为止。 因此,重点是那个『条件的达成』是什么。底下咱们就来谈一谈:
--------------------------------------------------------------------------------
while do done, until do done
通常来讲,循环最多见的就是底下这两种状态了:
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
相对于 while, until 的循环方式是必需要『符合某个条件』的状态, for 这种语法,则是『 已经知道要进行几回循环』的状态!他的语法是:
for (( 初始值; 限制值; 执行步阶 ))
do
程序段
done
这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为:
初始值:某个变量在循环当中的起始值,直接以相似 i=1 设定好;
限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如 i<=100;
执行步阶:每做一次循环时,变量的变化量。例如 i=i+1。
值得注意的是,在『执行步阶』的设定上,若是每次增长 1 ,则可使用相似『i++』的方式,亦便是 i 每次循环都会增长一的意思。好,咱们以这种方式来进行 1 累加到 100 的循环吧!
[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 的变量内容在循环工做时:
第一次循环时, $var 的内容为 con1 ;
第二次循环时, $var 的内容为 con2 ;
第三次循环时, $var 的内容为 con3 ;
....
咱们能够作个简单的练习。假设我有三种动物,分别是 dog, cat, elephant 三种, 我想每一行都输出这样:『There are dogs...』之类的字样,则能够:
[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
呵呵!颇有趣的例子吧~利用这种方式,您能够很轻易的来处理一些档案的特性呢~ 咱们循环就介绍到这里了~其它更多的应用,就得视您的需求来玩啰~。
--------------------------------------------------------------------------------
shell script 的追踪与 debug
scripts 在执行以前,最怕的就是出现问题了!那么咱们如何 debug 呢?有没有办法不须要透过直接执行该 scripts 就能够来判断是否有问题呢!?呵呵! 固然是有的!咱们就直接以 bash 的相关参数来进行判断吧!
[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 参数,就能够知道问题是发生在哪一行上面了!
熟悉 sh 的用法,将可使您在管理 Linux 的过程当中驾轻就熟!至于在 Shell scripts 的学习方法上面,须要『多看、多模仿、并加以修改为本身的样式!』 是最快的学习手段了!网络上有至关多的朋友在开发一些至关有用的 scripts ,如果您能够将对方的 scripts 拿来,而且改为适合本身主机的样子!那么学习的效果会是最快的呢!
另外,咱们 Linux 系统原本就有不少的启动 script ,若是您想要知道每一个 script 所表明的功能是什么? 能够直接以 vi 进入该 script 去查阅一下,一般马上就知道该 script 的目的了。 举例来讲,咱们的 Linux 里头有个文件名称为: /etc/init.d/portmap ,这个 script 是干吗用的? 利用 vi 去查阅最前面的几行字,他出现以下信息:
# 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 时, 若是是系统提供的,那么利用这个检查的方式,必定能够约略了解的啦! 加油的啰~ ^_^
另外,本章全部的范例均可以在 http://linux.vbird.org/linux_basic/0340bashshell-scripts/scripts.tgz 里头找到喔!加油~
--------------------------------------------------------------------------------
本章习题练习
( 要看答案请将鼠标移动到『答:』底下的空白处,按下左键圈选空白处便可察看 )
请创建一支 script ,当你执行该 script 的时候,该 script 能够显示: 1. 你目前的身份 (用 whoami ) 2. 你目前所在的目录 (用 pwd)
#!/bin/bash
echo -e "Your name is ==>; `whoami`"
echo -e "The current directory is ==>; `pwd`"
请自行创建一支程序,该程序能够用来计算『您还有几天能够过生日』啊??
#!/bin/bash
read -p "Pleas input your birthday (MMDD, ex>; 0709): " bir
now=`date +%m%d`
if [ "$bir" == "$now" ]; then
echo "Happy Birthday to you!!!"
elif [ "$bir" -gt "$now" ]; then
year=`date +%Y`
total_d=$(($((`date --date="$year$bir" +%s`-`date +%s`))/60/60/24))
echo "Your birthday will be $total_d later"
else
year=$((`date +%Y`+1))
total_d=$(($((`date --date="$year$bir" +%s`-`date +%s`))/60/60/24))
echo "Your birthday will be $total_d later"
fi
让使用者输入一个数字,程序能够由 1+2+3... 一直累加到使用者输入的数字为止。
#!/bin/bash
read -p "Please input an integer number: " number
i=0
s=0
while [ "$i" != "$number" ]
do
i=$(($i+1))
s=$(($s+$i))
done
echo "the result of '1+2+3+...$number' is ==>; $s"
撰写一支程序,他的做用是: 1.) 先查看一下 /root/test/logical 这个名称是否存在; 2.) 若不存在,则创建一个档案,使用 touch 来创建,创建完成后离开; 3.) 若是存在的话,判断该名称是否为档案,若为档案则将之删除后创建一个档案,档名为 logical ,以后离开; 4.) 若是存在的话,并且该名称为目录,则移除此目录!
#!/bin/bash
if [ ! -e logical ]; then
touch logical
echo "Just make a file logical"
exit 1
elif [ -e logical ] && [ -f logical ]; then
rm logical
mkdir logical
echo "remove file ==>; logical"
echo "and make directory logical"
exit 1
elif [ -e logical ] && [ -d logical ]; then
rm -rf logical
echo "remove directory ==>; logical"
exit 1
else
echo "Does here have anything?"
fi
咱们知道 /etc/passwd 里面以 : 来分隔,第一栏为帐号名称。请写一只程序,能够将 /etc/passwd 的第一栏取出,并且每一栏都以一行字符串『The 1 account is "root" 』来显示,那个 1 表示行数。
#!/bin/bash
accounts=`cat /etc/passwd | cut -d':' -f1`
for account in $accounts
do
declare -i i=$i+1
echo "The $i account is "$account" "
donelinux