什么是 shell script (程序化脚本) 呢?就字面上的意义,我们将他分为两部份。 在『 shell 』部分,我们在bash当中已经提过了,那是一个文字介面底下让我们与系统沟通的一个工具介面。那么『 script 』是啥? 字面上的意义, script 是『脚本、剧本』的意思。整句话是说, shell script 是针对 shell 所写的『剧本!』
什么东西啊?其实, shell script 是利用 shell 的功能所写的一个『程序 (program)』,这个程序是使用纯文字档,将一些 shell 的语法与命令(含外部命令)写在里面, 搭配正规表示法、管线命令与数据流重导向等功能,以达到我们所想要的处理目的。
shell script 更提供阵列、回圈、条件与逻辑判断等重要功能,让使用者也可以直接以 shell 来撰写程序,而不必使用类似 C 程序语言等传统程序撰写的语法呢!
在武侠世界中,不论是那个门派,要学武功要从扫地做起,那么要学程序呢?呵呵,肯定是由『秀出 Hello World!』 这个字眼开始的!OK!那么鸟哥就先写一支 script 给大家瞧一瞧:
[root@www ~]# mkdir scripts; cd scripts [root@www scripts]# vi sh01.sh #!/bin/bash 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 这个目录内, 未来比较好管理啦!上面的写法当中,鸟哥主要将整个程序的撰写分成数段,大致是这样:
第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:
因为我们使用的是 bash ,所以,必须要以『 #!/bin/bash 』来宣告这个文件内的语法使用 bash 的语法!那么当这个程序被运行时,他就能够加载 bash 的相关环境配置档 (一般来说就是 non-login shell 的 ~/.bashrc), 并且运行 bash 来使我们底下的命令能够运行!这很重要的!(在很多状况中,如果没有配置好这一行, 那么该程序很可能会无法运行,因为系统可能无法判断该程序需要使用什么 shell 来运行啊!)
程序内容的说明:
整个 script 当中,除了第一行的『 #! 』是用来宣告 shell 的之外,其他的 # 都是『注解』用途! 所以上面的程序当中,第二行以下就是用来说明整个程序的基本数据。一般来说, 建议你一定要养成说明该 script 的:1. 内容与功能; 2. 版本资讯; 3. 作者与联络方式; 4. 建档日期;5. 历史纪录 等等。这将有助於未来程序的改写与 debug 呢!
主要环境变量的宣告:
建议务必要将一些重要的环境变量配置好,鸟哥个人认为, PATH 与 LANG (如果有使用到输出相关的资讯时) 是当中最重要的! 如此一来,则可让我们这支程序在进行时,可以直接下达一些外部命令,而不必写绝对路径呢!比较好啦!
主要程序部分
就将主要的程序写好即可!在这个例子当中,就是 echo 那一行啦!
运行成果告知 (定义回传值)
是否记得我们讨论一个命令的运行成功与否,可以使用 $? 这个变量来观察~ 那么我们也可以利用 exit 这个命令来让程序中断,并且回传一个数值给系统。 在我们这个例子当中,鸟哥使用 exit 0 ,这代表离开 script 并且回传一个 0 给系统, 所以我运行完这个 script 后,若接著下达 echo $? 则可得到 0 的值喔! 更聪明的读者应该也知道了,呵呵!利用这个 exit n (n 是数字) 的功能,我们还可以自订错误信息, 让这支程序变得更加的 smart 呢!
接下来透过刚刚上头介绍的运行方法来运行看看结果吧!
[root@www scripts]# sh sh01.sh Hello World !
你会看到萤幕是这样,而且应该还会听到『咚』的一声,为什么呢?还记得前一章提到的 printf 吧?用 echo 接著那些特殊的按键也可以发生同样的事情~ 不过, echo 必须要加上 -e 的选项才行!呵呵!在你写完这个小 script 之后,你就可以大声的说:『我也会写程序了』!哈哈! 很简单有趣吧~ ^_^
另外,你也可以利用:『chmod a+x sh01.sh; ./sh01.sh』来运行这个 script 的呢!
当使用前一小节提到的直接命令下达 (不论是绝对路径/相对路径还是 $PATH 内),或者是利用 bash (或 sh) 来下达脚本时, 该 script 都会使用一个新的 bash 环境来运行脚本内的命令!也就是说,使用者种运行方式时, 其实 script 是在子程序的 bash 内运行的!父程序/子程序一些概念性的问题, 重点在於:『当子程序完成后,在子程序内的各项变量或动作将会结束而不会传回到父程序中』! 这是什么意思呢?
我们举刚刚提到过的 sh02.sh 这个脚本来说明好了,这个脚本可以让使用者自行配置两个变量,分别是 firstname 与 lastname,想一想,如果你直接运行该命令时,该命令帮你配置的 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 来运行命令那就不一样了!同样的脚本我们来运行看看:
[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 』是一样的啊!
一个良好习惯的养成是很重要的~大家在刚开始撰写程序的时候,最容易忽略这部分, 认为程序写出来就好了,其他的不重要。其实,如果程序的说明能够更清楚,那么对你自己是有很大的帮助的。
举例来说,鸟哥自己为了自己的需求,曾经撰写了不少的 script 来帮我进行主机 IP 的侦测啊、 登录档分析与管理啊、自动上传下载重要配置档啊等等的,不过,早期就是因为太懒了, 管理的主机又太多了,常常同一个程序在不同的主机上面进行更改,到最后,到底哪一支才是最新的都记不起来, 而且,重点是,我到底是改了哪里?为什么做那样的修改?都忘的一干二净~真要命~
所以,后来鸟哥在写程序的时候,通常会比较仔细的将程序的设计过程给他记录下来,而且还会记录一些历史纪录, 如此一来,好多了~至少很容易知道我修改了哪些数据,以及程序修改的理念与逻辑概念等等, 在维护上面是轻松很多很多的喔!
另外,在一些环境的配置上面,毕竟每个人的环境都不相同,为了取得较佳的运行环境, 我都会自行先定义好一些一定会被用到的环境变量,例如 PATH 这个玩意儿! 这样比较好啦~所以说,建议你一定要养成良好的 script 撰写习惯,在每个 script 的档头处记录好:
script 的功能;
script 的版本资讯;
script 的作者与联络方式;
script 的版权宣告方式;
script 的 History (历史纪录);
script 内较特殊的命令,使用『绝对路径』的方式来下达;
script 运行时需要的环境变量预先宣告与配置。
除了记录这些资讯之外,在较为特殊的程序码部分,个人建议务必要加上注解说明,可以帮助你非常非常多!
转自 http://vbird.dic.ksu.edu.tw/linux_basic/0340bashshell-scripts_1.php