Linux学习笔记(十四)

学习shell scripts

shell script执行方法(现在我们假设你写的这个程序文件名是 /home/dmtsai/shell.sh ):

 直接指令下达: shell.sh 档案必须要具备可读与可执行 (rx) 的权限,然后:  

绝对路径:使用 /home/dmtsai/shell.sh 来下达指令; 

相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来执行 

变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/ 

以 bash 程序来执行:透过『 bash shell.sh 』或『 sh shell.sh 』来执行 

撰写第一支 script 

在本章当中,请将所有撰写的 script 放置到你家目录的 ~/scripts 这个目录内, 未来比较好管理啦!

上面的写法当中,鸟哥主要将整个程序的撰写分成数段,大致是这样: 

1.  第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:因为我们使用的是 bash ,所以,必须要以『 #!/bin/bash 』来宣告这个档案内的语法使用bash 的语法!那样当这个程序被执行时,他就能够加载 bash 的相关环境配置文件 (一般来说就是 non-login shell 的 ~/.bashrc), 并且执行 bash 来使我们底下的指令能够执行!这很重要的!(在很多状况中,如果没有设定好这一行, 那么该程序很可能会无法执行,因为系统可能无法判断该程序需要使用什么 shell 来执行啊!

2.  程序内容的说明:整个 script 当中,除了第一行的『 #! 』是用来宣告 shell 的之外,其他的 都是『批注』用途! 所以上面的程序当中,第二行以下就是用来说明整个程序的基本数据。一般来说, 建议你一定要养成说明该 script 的:1. 内容与功能; 2. 版本信息; 3. 作者与联系方式; 4. 建档日期;5. 历史纪录 等等。这将有利于未来程序的改写与 debug 呢! 

3.  主要环境变量的宣告:建议务必要将一些重要的环境变量设定好,鸟哥个人认为, PATH 与 LANG (如果有使用到输出相关的信息时是当中最重要的! 如此一来,则可让我们这支程序在进行时,可以直接下达一些外部指令,而不必写绝对路径呢!比较好啦! 

4.  主要程序部分:就将主要的程序写好即可!在这个例子当中,就是 echo 那一行啦! 

5.  执行成果告知 (定义回传值:是否记得我们在第十一章里面要讨论一个指令的执行成功与否,可以使用 $? 这个变量来观察~ 那么我们也可以到用 exit 这个指令来让程序中断,并且回传一个数值给系统。 在我们这个例子当中,鸟哥使用 exit 0 ,这代表离开 script 并且回传一个 给系统, 所以我执行完这个 script 后,若接着下达 echo $? 则可得到 的值喔! 更聪明的读者应该也知道了,呵呵!调用这个 exit n (n 是数字的功能,我们还可以自定义错误讯息, 让这支程序变得更加的 smart 呢! 

接下来透过刚刚上头介绍的执行方法来执行吧! 

[root@www scripts]# sh sh01.sh 

 

对谈式脚本:变量内容由用户决定 

利用直接执行的方式来执行 script『当子程序完成后,在子程序内的各项变量或动作将会结束而不会传回到父程序中』我们举刚刚提到过的 sh02.sh 这个脚本来说明好了,这个脚本可以让用户自行设定两个变量,分别是firstnamelastname,想一想,如果你直接执行该指令时,该指令帮你设定的 firstname 会不会生效?看一下底下的执行结果: 

[root@www scripts]# echo $firstname $lastname 

    <==确认了,这两个变量并不存在喔! 

[root@www scripts]# sh sh02.sh 

Please input your first name: VBird <==这个名字是鸟哥自己输入的 

Please input your last name:  Tsai  

Your full name is: VBird Tsai      <==看吧!在 script 运作中,这两个变数有生效 

[root@www scripts]# echo $firstname $lastname 

<==事实上,这两个变量在父程序的 bash 中还是不存在的!

上面的结果你应该会觉得很奇怪,怎么我已经调用 sh02.sh 设定好的变量竟然在 bash 环境底下无效!怎么回事呢? 如果将程序相关性绘制成图的话,我们以下图来说明。当你使用直接执行的方法来处理时,系统会给予一支新的 bash 让我们来执行 sh02.sh 里面的指令,因此你的 firstname, lastname 等变量其实是在下图中的子程序 bash 内执行的。 当 sh02.sh 执行完毕后,子程序 bash 内的所有数据便被移除,因此上表的练习中,在父程序底下 echo $firstname 时, 就看不到任何东西了!

 

调用 source 来执行脚本:在父程序中执行  

如果你使用 source 来执行指令那就不一样了!同样的脚本我们来执行看看: 

[root@www scripts]# source sh02.sh 

Please input your first name: VBird 

Please input your last name:  Tsai 

Your full name is: VBird Tsai 

[root@www scripts]# echo $firstname $lastname 

VBird Tsai  <==嘿嘿!有数据产生喔!  

竟然生效了!没错啊!因为 source 对 script 的执行方式可以使用底下的图示来说明! sh02.sh 会在父程序中执行的,因此各项动作都会在原本的 bash 内生效!这也是为啥你不注销系统而要让某些写入 ~/.bashrc 的设定生效时,需要使用『 source ~/.bashrc 』而不能使用『 bash ~/.bashrc 』是一样的啊! 

 

调用 test 指令的测试功能 

当我要检测系统上面某些档案或者是相关的属性时,调用 test 这个指令来工作真是好用得不得了,举例来说,我要检查 /dmtsai 是否存在时,使用: 

[root@www ~]# test -e /dmtsai 

执行结果并不会显示任何讯息,但最后我们可以透过 $? 或 && 及 || 来展现整个结果呢! 例如我们在将上面的例子改写成这样: 

[root@www ~]# test -e /dmtsai && echo "exist" || echo "Not exist" 

Not exist  <==结果显示不存在啊! 

最终的结果可以告知我们是『exist』还是『Not exist』呢!那我知道 -e 是测试一个『东西』在不在,如果还想要测试一下该档名是啥玩意儿时,还有哪些标志可以来判断的呢?呵呵!有底下这些东西喔! 

现在我们就调用 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" 

 

调用判断符号 [ ] 

除了我们很喜欢使用的 test 之外,其实,我们还可以调用判断符号『 [ ] (就是中括号啦来进行数据的判断呢!举例来说,如果我想要知道 $HOME 这个变量是否为空的,可以这样做: 

[root@www ~]# [ -z "$HOME" ] ; echo $? 

使用中括号必须要特别注意,因为中括号用在很多地方,包括通配符与正则表示法等等,所以如果要在bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空格符来分隔喔! 假设我空格键使用『□』符号来表示,那么,在这些地方你都需要有空格键: 

[  "$HOME"  ==  "$MAIL"  ] 

["$HOME"=="$MAIL"

 ↑                    ↑       ↑                  ↑ 

你最好要注意: 

·  在中括号 [] 内的每个组件都需要有空格键来分隔; 

·  在中括号内的变数,最好都以双引号括号起来; 

·  在中括号内的常数,最好都以单或双引号括号起来。 

另外,中括号的使用方法与 test 几乎一模一样啊~ 只是中括号比较常用在条件判断式 if ..... then ..... fi 的情况中就是了。

[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 

由于是任何一个成立即可 (大写或小写的 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 内使用来呼叫这些参数喔! 

·  $# :代表后接的参数『个数』,以上表为例这里显示为『 』; 

·  $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来); 

·  $* :代表『 "$1c$2c$3c$4" 』,其中 为分隔字符,默认为空格键, 所以本例中代表『 "$1 $2 $3 $4" 』之意。 

............................................................................

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:造成参数变量号码偏移 

 ............................................................................

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' 

 

条件判断式 

利用 if .... then 

单层、简单条件判断式 

如果你只有一个判断式要进行,那么我们可以简单的这样看: 

if [ 条件判断式 ]; then 

  当条件判断式成立时,可以进行的指令工作内容; 

fi   <==将 if 反过来写,就成为 fi 啦!结束 if 之意! 

至于条件判断式的判断方法,与前一小节的介绍相同啊!较特别的是,如果我有多个条件要判别时,除了 sh06.sh 那个案例所写的,也就是『将多个条件写入一个中括号内的情况』之外, 我还可以有多个中括号来隔开喔!而括号与括号之间,则以 && 或 || 来隔开,他们的意义是: 

·  && 代表 AND ; 

·  || 代表 or ; 

所以,在使用中括号的判断式中, && 及 || 就与指令下达的状态不同了。举例来说, sh06.sh 里面的判断式可以这样修改: 

[ "$yn" == "Y" -o "$yn" == "y" ] 

上式可替换为 [ "$yn" == "Y" ] || [ "$yn" == "y" ] 

....................................................................

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 

 

多重、复杂条件判断式 

一个条件判断,分成功进行与失败进行 (else) 

if [ 条件判断式 ]; then 

  当条件判断式成立时,可以进行的指令工作内容; 

else 

  当条件判断式不成立时,可以进行的指令工作内容; 

fi 

如果考虑更复杂的情况,则可以使用这个语法: 

多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行 

if [ 条件判断式一 ]; then 

  当条件判断式一成立时,可以进行的指令工作内容; 

elif [ 条件判断式二 ]; then 

  当条件判断式二成立时,可以进行的指令工作内容; 

else 

  当条件判断式一与二均不成立时,可以进行的指令工作内容; 

fi 

.....................................................................

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 your choice is" 

fi 

 

利用 case ..... esac 判断 

case  $变量名称 in   <==关键词为 case ,还有变数前有钱字号

"第一个变量内容")   <==每个变量内容建议用双引号括起来,关键词则为小括号 

  程序段 

  ;;             <==每个类别结尾使用两个连续的分号来处理! 

  "第二个变量内容") 

  程序段 

  ;; 

  *)                 <==最后一个变量内容都会用 来代表所有其他值不包扩第一个变量内容与第二个变量内容的其他程序执行段 

  exit 1 

  ;; 

esac                  <==最终的 case 结尾!『反过来写』思考一下! 

小例子如下:

...............................................

case $1 in 

  "hello") 

  echo "Hello, how are you ?" 

  ;; 

  "") 

  echo "You MUST input parameters, ex> {$0 someword}" 

  ;; 

  *)   # 其实就相当于通配符,0~无穷多个任意字符之意! 

  echo "Usage $0 {hello}" 

  ;; 

esac 

 

利用 function 功能 

function 的语法是这样的: 

function fname() { 

  程序段 

}

小例子如下:

................................................................

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 

function 也是拥有内建变量的~他的内建变量与 shell script 很类似, 函数名称代表示 $0 ,而后续接的变量也是以 $1, $2... 来取代的~ 这里很容易搞错喔~因为『 function fname() { 程序段 』内的 $0, $1... 等等与 shell script 的 $0 是不同的

......................................................................................

function printit(){ 

  echo "Your choice is $1"   # 这个 $1 必须要参考底下指令的下达 

 

echo "This program will print your selection !" 

case $1 in 

  "one") 

  printit 1  # 请注意, printit 指令后面还有接参数! 

  ;; 

  "two") 

  printit 2 

  ;; 

  "three") 

  printit 3 

  ;; 

  *) 

  echo "Usage $0 {one|two|three}" 

  ;; 

esac 

 

循环 (loop) 

while do done, until do done (不定循环

一般来说,不定循环最常见的就是底下这两种状态了: 

while [ condition ]  <==中括号内的状态就是判断式 

do            <==do 是循环的开始! 

 程序段落 

done          <==done 是循环的结束 

while 的中文是『当....时』,所以,这种方式说的是『当 condition 条件成立时,就进行循环,直到condition 的条件不成立才停止』的意思。

还有另外一种不定循环的方式: 

until [ condition ] 

do 

  程序段落 

done 

这种方式恰恰与 while 相反,它说的是『当 condition 条件成立时,就终止循环, 否则就持续进行循环的程序段。』

........................................................................

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." 

...........................................................................

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.  .............. 

...................................................

for animal in dog cat elephant 

do 

  echo "There are ${animal}s.... " 

done 

 

for...do...done 的数值处理 

除了上述的方法之外,for 循环还有另外一种写法!语法如下: 

for (( 初始值限制值执行步阶 )) 

do 

  程序段 

done 

这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为: 

·  初始值:某个变量在循环当中的起始值,直接以类似 i=1 设定好; 

·  限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如 i<=100; 

·  执行步阶:每作一次循环时,碧昂量的变化量。例如 i=i+1。 

...........................................

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" 

 

shell script 的追踪与 debug 

[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.... 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值