1.5.1. 脚本的必要元素
本指南主要介绍最后一个 shell 构建模块———脚本。对于脚本有些考虑:
- 脚本应该没有错误地运行。
- 它应该执行预期的任务。
- 程序逻辑定义明确且显而易见
- 脚本不会做不必要的工作。
- 脚本应该是可重用的。
1.5.2. 结构
shell 脚本的结构可以非常灵活。尽管在 Bash 中赋予了很多自由,但您必须确保正确的逻辑、流程控制和效率,以便执行脚本的用户可以轻松正确地执行此操作。
开始编写新脚本时,问自己以下问题:
- 我需要来自用户或用户环境的任何信息吗?
- 我将如何存储这些信息?
- 是否有需要创建的文件?在哪里以及拥有哪些权限和所有权?
- 我将使用什么命令?在不同系统上使用脚本时,所有这些系统是否都有所需版本的这些命令?
- 用户是否需要任何通知?什么时候,为什么?
1.5.3. 术语
下表概述了您需要熟悉的编程术语:
Table 1-1. 编程术语概述
Term | What is it? |
---|---|
Command control | 测试命令的退出状态以确定是否正确执行程序。 |
Conditional branch | 当一个条件决定接下来会发生什么时,程序中的逻辑点。 |
Logic flow | 程序的整体设计。确定任务的逻辑顺序。 |
Loop | 执行零次或多次的程序。 |
User input | 程序运行时由外部来源提供的信息,该信息可以在需要时存储和调用。 |
1.5.4. 关于顺序和逻辑的一些话
为了加快开发过程,应该提前考虑程序的逻辑顺序。这是开发脚本的第一步。
可以使用多种方法;最常见的一种是使用列表。逐项列出程序中涉及的任务列表可以让您描述每个过程。单个任务可以通过它们的项目编号来标识。
使用您自己的语言来确定程序要执行的任务将有利于创建好理解的程序。稍后,您可以将日常语言语句替换为 shell 语言单词和结构。
下面的示例显示了这样的逻辑流程设计。它描述了日志文件的轮换。此示例显示了一个可能的重复循环:
- 你想轮换日志吗?
- 如果是:
- 输入包含要轮换的日志的目录名称。
- 输入日志文件的名称。
- 输入日志应保留的天数。
- 使设置永久保存在用户的 crontab 文件中。
- 如果否,请转到步骤 3。
- 如果是:
- 您要轮换另一组日志吗?
- 如果是:重复步骤 1。
- 如果否,请转到步骤 3。
- 退出
用户提供信息让程序做某事。必须获取并存储来自用户的输入。应通知用户他的 crontab 更改。
1.5.5. 一个示例: mysystem.sh
下面的mysystem.sh脚本执行一些众所周知的命令(date、w、uname、uptime)来显示有关您和您的机器的信息。
tom:~> **cat \-n mysystem.sh**
1 #!/bin/bash
2 clear
3 echo "This is information provided by mysystem.sh. Program starts now."
4
5 echo "Hello, $USER"
6 echo
7
8 echo "Today's date is \`date\`, this is week \`date +"%V"\`."
9 echo
10
11 echo "These users are currently connected:"
12 w | cut -d " " -f 1 - | grep -v USER | sort -u
13 echo
14
15 echo "This is \`uname -s\` running on a \`uname -m\` processor."
16 echo
17
18 echo "This is the uptime information:"
19 uptime
20 echo
21
22 echo "That's all folks!"
脚本始终以相同的两个字符“#!”开头 . 之后,将执行第一行之后的命令。该脚本第 2 行清除屏幕。第 3 行使其打印一条消息,通知用户将要发生的事情。第 5 行问候用户。第 6、9、13、16 和 20 行仅用于有序输出。第 8 行打印当前日期和周数。第 11 行同样是一条信息性消息,如第 3、18 和 22 行。第 12 行格式化w的输出;第 15 行显示操作系统和 CPU 信息。第 19 行给出了正常运行时间和负载信息。
echo和printf的都是Bash内建命令。第一个总是以 0 状态退出,并且只是在标准输出上打印参数后跟一个行尾字符,而后者允许定义格式化字符串并在失败时给出非零退出状态代码。
这是使用printf的相同脚本:
tom:~> **cat mysystem.sh**
#!/bin/bash
clear
printf "This is information provided by mysystem.sh. Program starts now.\\n"
printf "Hello, $USER.\\n\\n"
printf "Today's date is \`date\`, this is week \`date +"%V"\`.\\n\\n"
printf "These users are currently connected:\\n"
w | cut -d " " -f 1 - | grep -v USER | sort -u
printf "\\n"
printf "This is \`uname -s\` running on a \`uname -m\` processor.\\n\\n"
printf "This is the uptime information:\\n"
uptime
printf "\\n"
printf "That's all folks!\\n"
Bourne Again shell 的标准位置:/bin
如果标准输出不可用
如果您从 cron 执行脚本,请提供完整路径名并重定向输出和错误。由于 shell 在非交互模式下运行,如果您不考虑这一点,任何错误都会导致脚本过早退出。 |
---|
1.5.6. 示例初始化脚本
初始化脚本在 UNIX 和 Linux 机器上启动系统服务。系统日志守护程序、电源管理守护程序、名称和邮件守护程序是常见的示例。这些脚本也称为启动脚本,存储在系统上的特定位置,例如/etc/rc.d/init.d或/etc/init.d。Init 是初始进程,它读取其配置文件并决定在每个运行级别中启动或停止哪些服务。运行级别是进程;每个系统都有一个用户运行级别,例如,用于执行管理任务,系统必须尽可能处于未使用状态,例如从备份中恢复关键文件系统。重新启动和关闭运行级别通常也被配置。
启动或停止服务时要执行的任务列在启动脚本中。配置init是系统管理员的任务之一,以便在正确的时刻启动和停止服务。面对此任务时,您需要很好地了解系统上的启动和关闭过程。因此,我们建议您在开始自己的初始化脚本之前阅读init和inittab的手册页。
这是一个非常简单的示例,它将在启动和停止机器时播放声音:
#!/bin/bash
# This script is for /etc/rc.d/init.d
# Link in rc3.d/S99audio-greeting and rc0.d/K01audio-greeting
case "$1" in
'start')
cat /usr/share/audio/at\_your\_service.au > /dev/audio
;;
'stop')
cat /usr/share/audio/oh\_no\_not\_again.au > /dev/audio
;;
esac
exit 0