# 什么是 Shell Scripts
Shell Scripts :程序化脚本;
- shell :在前面第十章中讲过的 BASH,是一个文字接口让我们与系统沟通的一个工具接口。
- script:脚本
那么就是针对 shell 写的脚本
shell script 可以简单的看成是批处理文件,也可以称为一种程序语言,该语言是利用 shell 与相关工具指令,所以不需要编译即可执行,且有不错的 debug 工具,所以,它可以帮助系统管理员快速的管理好主机
# 为什么要学习 shell scripts?
简单说:想要玩清楚 Linux 的来龙去脉,shell script 是必须的知识,因为:
自动化管理的重要依据
管理一部主机每天要进行的任务就有:
- 查询登录文件
- 追踪流量
- 监控用户使用主机状态
- 主机各项硬件设备状态
- 主机软件更新查询
等等,这里白不包括有其他使用者突然的要求了。这些工作进行又可以分为:
- 自行手动处理
- 写个简单的程序来帮你每日「自动处理分析」
追踪与管理系统的重要工作
在 CentOS 6.x 以前的版本中,系统的服务(services)启动的接口是在
/etc/init.d
目录下,所有文件都是 scripts;另外,包括开机(booting)过程也是利用 shell script 来帮忙搜索系统的相关设置数据,再代入各个服务的设置参数。 比如:想要重新启动系统注册表,可以使用
/etc/init.d/rsyslogd restart
rsyslogd 文件就是 script 了 另外,比如 Mysql 数据库服务启动时,有可能就在 script 中主动以「空密码」尝试登陆 Mysql,为了安全性,那么你就可以修改这个 script 文件。
虽然
/etc/init.d/*
这个脚本目前的启动方式(systemV)已经被新一代的 systemd 所代替了(从 CentOS 7 开始),但是很多的个别服务在管理他们的服务启动方面,还是使用 shell script 的机制简单入侵检测功能
当系统有异常状态时,大多会讲这些记录在「系统注册表」中(系统记录器),那么就可以在固定的几分钟内主动的去分析注册表文件,若察觉有问题,就立刻通知管理员,或者是立刻加强防火墙的规则,如此一来,主机就能过达到自我保护的聪明学习功能了。
比如:可以通过 shell script 分析「当该封包尝试几次还是联机失败之后,就抵挡住该 IP」之类的动作
连续指令单一化
简单说,script 最简单的功能就是,将一批指令写入 script 中,达到执行一个文件就能下达一批指令的目的。
比如:防火墙连续规则(iptables)、开机加载程序的项目(/etc/rc.d/rc.local) 等等
简易的数据处理
前面几章讲解的如 awk 等指令就可以用来处理简单的数据。配合各种指令来达到处理数据的目的
跨平台支持与学习历程较短
几乎所有的 Unix Like 上都可以运行 shell script,连 MS Windows 系列也有相关的 script 仿真器可以用
虽然 shell script 号称是程序,实际上,shell script 处理数据的速度上还是不够快,因为用的是外部的指令与 bash shell 的一些默认工具,所以常常去调用外部的函数库,因此指令周期上面比不上传统的程序语言
所以,shell script 用在系统管理上是很好的一项工具,但是用在处理大量数值运算上,就不行了,速度较慢,使用 CPU 资源较多,造成主机资源的分配不良。我们通常利用 shell script 来处理服务器的侦测就比较合适
# 第一支 script 的编写与执行
shell script 是纯文本文件,可以在里面一次性执行多个指令,或者是利用一些运算与逻辑判断来帮助我们达成某些功能。所以需要具备 bash 指令下达相关知识(第四章中开始下达指令中讲过),除此之外,还有以下知识需要了解:
- 指令的执行是从上而下、从左而右的分析与执行
- 指令的下达:指令、选项与参数间的多个空白都会被忽略掉
- 空白行也将被忽略,并且「tab」按键锁推开的空白行同样视为空格
- 如果读取到一个 enter 符号(CR),就尝试开始执行改行(或该串)命令
- 如果一行内容太多,则可以使用「
\[Enter]
」来延伸至下一行 #
可作为批注。任何加在#
后面的文字将被视为批注文字而被忽略
假设现在存在一个 script 是 /home/mrcode/shell.sh
,有如下的方式执行这个文件
- 直接指令下达:shell.sh 文件必须有可读与执行权限(rx)
- 绝对路径:使用
/home/mrcode/shell.sh
执行 - 相对路径:假设工作目录在
/home/mnrcode
,就使用./shell.sh
执行 - 变量「PATH」功能:将 shell.sh 放在 PATH 指定目录内,例如 ~
/bin/
- 绝对路径:使用
- 以 bash 程序来执行:
bash shell.sh
或sh shell.sh
执行
至于那个相对路径 ./shell.sh
为什么需要这样,是因为 路径与指令搜索顺序 的关系;
sh shell.sh
为啥可以执行?
[mrcode@study ~]$ type -a sh
sh is /usr/bin/sh
[mrcode@study ~]$ ll /usr/bin/sh
lrwxrwxrwx. 1 root root 4 Jan 17 14:32 /usr/bin/sh -> bash
2
3
4
可以看到 sh 是 bash 的链接文件,同时还可以使用参数 -n 和 -x
来检查与追踪 shell.sh 的语法是否正确
# Hello World
先来一个 Hello World 脚本,再来逐步说明
[mrcode@study ~]$ pwd
/home/mrcode
[mrcode@study ~]$ mkdir bin; cd bin
[mrcode@study bin]$ vi hello.sh
#!/bin/bash
# Program:
# This program shows "Hello World" in your screen.
# HIstory:
# 2020/02/19 mrcode first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0
2
3
4
5
6
7
8
9
10
11
12
13
14
在本章中,请将所有编写的 script 放置到你家目录的 ~/bin
目录内,未来比较好管理,针对如上脚本的写法,分段说明:
#!/bin/bash
:宣告这个 script 使用的 shell 名称因为我们使用的是 bash,所以必须以
#!/bin/bash
来声明该文件内的语法使用 bash 语法。当这个程序被执行时,能够加载 bash 的相关环境配置文件(一般来说是 non-login shell 的
~/.bashrc
),并且执行 bash 来使指令能够执行。很多情况下导致无法执行可能就是因为这一行的原因,系统无法判断该 sh 文件使用什么 shell 来执行程序内容说明
整个文件中,除了第一行的
#!
是用来声明 shell 之外,其他的#
都是批注信息。一般来说,建议一定要养成说明该 scrip 的:- 内容与功能
- 版本信息
- 作者与联络方式
- 建档日期
- 历史记录
等等,这将有助于未来程序的改写与 debug
主要环境变量的声明
建议务必将一些重要的环境变量设置好,PATH 与 LANG(输出相关信息时) 是当中最重要的,如此一来就可以直接下达外部指令,而不用写绝对路径,比较方便
主要程序部分
在本例中,就是 echo 那一行
执行结果告知(定义回传值)
指令回传值 中讲解到,可以使用
$?
来观察,那么可以利用 exit 这个指令来让程序中断,并且回传一个数值给系统。本例中使用的是
exit 0
,表示离开 script 并且回传一个 0 给系统,所以执行完这个 shell.sh 之后,下达echo $?
则可以得到 0 的值。利用这个 exit n(n 是数字)的功能,还可以自定义错误信息,让这支程序变得更加智能
执行与观察结果
# 观察权限,目前没有 x 执行权限
[mrcode@study bin]$ ll
total 4
-rw-rw-r--. 1 mrcode mrcode 239 Jan 19 11:25 hello.sh
# 尝试执行,报错无权限
[mrcode@study bin]$ ./hello.sh
-bash: ./hello.sh: Permission denied
# 添加执行权限
[mrcode@study bin]$ chmod u+x hello.sh
[mrcode@study bin]$ ll
total 4
-rwxrw-r--. 1 mrcode mrcode 239 Jan 19 11:25 hello.sh
# 执行脚本
[mrcode@study bin]$ ./hello.sh
Hello World!
# 查看回传值
[mrcode@study bin]$ echo $?
0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 编写 shell script 的良好习惯建立
一个良好习惯的养成至关重要,往往最开始时最容易忽视这部分的,觉得程序只要写出来就可以了,但是随着时间的拉长,不断的维护和修改。后续维护就可能出现问题
比如:作者管理很多计算机,由于太懒,经常同一个程序在不同的主机上进行修改,最最后也不知道哪一个程序是最新的,其中做了什么修改,又为什么做那样的修改。
所以,在写程序时,需要仔细的将程序的设计过程记录下来,而且还会记录一些历史记录,这样会导致维护成本降低
另外,在一些环节设置上面,毕竟每个人的环境都不相同,为了取得较佳的执行环境,一般都会预先定义一些一定会被用到的环境变量,例如上面的 PATH。因此养成良好的 script 编写习惯,建议在每个 script 文件头记录如下信息:
- 功能描述
- 版本信息
- 作者与联系方式
- 版权信息
- 历史记录(History)
- script 内较为特殊的指令,使用「绝对路径」方式来下达
- script 运作时需要的环境变量预先声明与设置
除了这些信息之外,在关键和难理解的代码部分添加批注信息。另外推荐代码编排格式使用 「巢状方式」,使用 tab 来缩进。编写 script 的工具是 vim 而不是 vi,因为 vim 有额外的语法校验机制