# 什么是 Shell Scripts

Shell Scripts :程序化脚本;

- shell :在前面第十章中讲过的 BASH,是一个文字接口让我们与系统沟通的一个工具接口。
- script:脚本

那么就是针对 shell 写的脚本

shell script 可以简单的看成是批处理文件,也可以称为一种程序语言,该语言是利用 shell 与相关工具指令,所以不需要编译即可执行,且有不错的 debug 工具,所以,它可以帮助系统管理员快速的管理好主机

# 为什么要学习 shell scripts?

简单说:想要玩清楚 Linux 的来龙去脉,shell script 是必须的知识,因为:

  • 自动化管理的重要依据

    管理一部主机每天要进行的任务就有:

    • 查询登录文件
    • 追踪流量
    • 监控用户使用主机状态
    • 主机各项硬件设备状态
    • 主机软件更新查询

    等等,这里白不包括有其他使用者突然的要求了。这些工作进行又可以分为:

    1. 自行手动处理
    2. 写个简单的程序来帮你每日「自动处理分析」
  • 追踪与管理系统的重要工作

    ​ 在 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 指令下达相关知识(第四章中开始下达指令中讲过),除此之外,还有以下知识需要了解:

  1. 指令的执行是从上而下、从左而右的分析与执行
  2. 指令的下达:指令、选项与参数间的多个空白都会被忽略掉
  3. 空白行也将被忽略,并且「tab」按键锁推开的空白行同样视为空格
  4. 如果读取到一个 enter 符号(CR),就尝试开始执行改行(或该串)命令
  5. 如果一行内容太多,则可以使用「\[Enter]」来延伸至下一行
  6. #可作为批注。任何加在 #后面的文字将被视为批注文字而被忽略

假设现在存在一个 script 是 /home/mrcode/shell.sh,有如下的方式执行这个文件

  • 直接指令下达:shell.sh 文件必须有可读与执行权限(rx)
    • 绝对路径:使用 /home/mrcode/shell.sh执行
    • 相对路径:假设工作目录在 /home/mnrcode,就使用 ./shell.sh执行
    • 变量「PATH」功能:将 shell.sh 放在 PATH 指定目录内,例如 ~/bin/
  • 以 bash 程序来执行:bash shell.shsh 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
1
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在本章中,请将所有编写的 script 放置到你家目录的 ~/bin 目录内,未来比较好管理,针对如上脚本的写法,分段说明:

  1. #!/bin/bash:宣告这个 script 使用的 shell 名称

    因为我们使用的是 bash,所以必须以 #!/bin/bash 来声明该文件内的语法使用 bash 语法。

    当这个程序被执行时,能够加载 bash 的相关环境配置文件(一般来说是 non-login shell 的 ~/.bashrc),并且执行 bash 来使指令能够执行。很多情况下导致无法执行可能就是因为这一行的原因,系统无法判断该 sh 文件使用什么 shell 来执行

  2. 程序内容说明

    整个文件中,除了第一行的 #! 是用来声明 shell 之外,其他的 #都是批注信息。一般来说,建议一定要养成说明该 scrip 的:

    1. 内容与功能
    2. 版本信息
    3. 作者与联络方式
    4. 建档日期
    5. 历史记录

    等等,这将有助于未来程序的改写与 debug

  3. 主要环境变量的声明

    建议务必将一些重要的环境变量设置好,PATH 与 LANG(输出相关信息时) 是当中最重要的,如此一来就可以直接下达外部指令,而不用写绝对路径,比较方便

  4. 主要程序部分

    在本例中,就是 echo 那一行

  5. 执行结果告知(定义回传值)

    指令回传值 中讲解到,可以使用 $? 来观察,那么可以利用 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

1
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 有额外的语法校验机制