本指南主要介绍 shell 的最后一个构建块:脚本。在我们继续之前,先考虑一些一般性问题
脚本应该无错误运行。
它应该执行其预期的任务。
程序逻辑应清晰明确。
脚本不应执行不必要的工作。
脚本应该是可重用的。
shell 脚本的结构非常灵活。即使 Bash 赋予了很大的自由度,您也必须确保正确的逻辑、流程控制和效率,以便执行脚本的用户可以轻松且正确地执行操作。
在开始编写新脚本时,请问自己以下问题
我是否需要从用户或用户的环境中获取任何信息?
我将如何存储这些信息?
是否需要创建任何文件?在哪里创建?权限和所有权是什么?
我将使用哪些命令?在不同的系统上使用脚本时,所有这些系统是否都具有所需版本的命令?
用户是否需要任何通知?何时需要,为什么需要?
为了加快开发过程,应预先考虑程序的逻辑顺序。这是您开发脚本的第一步。
可以使用多种方法;最常见的方法之一是使用列表。列出程序中涉及的任务列表允许您描述每个过程。各个任务可以通过其项目编号引用。
使用您自己的口语来确定程序要执行的任务将帮助您创建程序的可理解形式。稍后,您可以将日常语言语句替换为 shell 语言单词和结构。
下面的示例显示了这样的逻辑流程设计。它描述了日志文件的轮换。此示例显示了一个可能的重复循环,由您要轮换的基础日志文件数量控制
您要轮换日志吗?
如果 是
输入包含要轮换的日志的目录名称。
输入日志文件的基本名称。
输入日志应保留的天数。
在用户的 crontab 文件中进行永久设置。
如果 否,转到步骤 3。
您要轮换另一组日志吗?
如果 是:重复步骤 1。
如果 否:转到步骤 3。
退出
用户应提供信息以供程序执行某些操作。必须获取并存储来自用户的输入。应通知用户他们的 crontab 将会更改。
下面的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!" |
脚本总是以相同的两个字符 "#!" 开头。之后,定义将执行第一行之后命令的 shell。此脚本从清除第 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" |
通过插入消息创建用户友好的脚本在 第 8 章 中讨论。
![]() | Bourne Again shell 的标准位置 |
---|---|
这意味着 bash 程序安装在/bin. |
![]() | 如果 stdout 不可用 |
---|---|
如果您从 cron 执行脚本,请提供完整路径名并重定向输出和错误。由于 shell 在非交互模式下运行,如果您不考虑这一点,任何错误都会导致脚本过早退出。 |
以下章节将讨论上述脚本的详细信息。
init 脚本在 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 |
在这种脚本中经常使用的 case 语句在 7.2.5 节 中描述。