1.5. 编写优秀脚本

1.5.1. 优秀脚本的特性

本指南主要介绍 shell 的最后一个构建块:脚本。在我们继续之前,先考虑一些一般性问题

  1. 脚本应该无错误运行。

  2. 它应该执行其预期的任务。

  3. 程序逻辑应清晰明确。

  4. 脚本不应执行不必要的工作。

  5. 脚本应该是可重用的。

1.5.2. 结构

shell 脚本的结构非常灵活。即使 Bash 赋予了很大的自由度,您也必须确保正确的逻辑、流程控制和效率,以便执行脚本的用户可以轻松且正确地执行操作。

在开始编写新脚本时,请问自己以下问题

1.5.3. 术语

下表概述了您需要熟悉的编程术语

表 1-1. 编程术语概述

术语它是什么?
命令控制测试命令的退出状态,以确定是否应执行程序的一部分。
条件分支程序中的逻辑点,条件决定下一步发生什么。
逻辑流程程序的总体设计。确定任务的逻辑顺序,以便结果是成功且可控的。
循环程序中执行零次或多次的部分。
用户输入程序运行时由外部源提供的信息,可以存储并在需要时调用。

1.5.4. 关于顺序和逻辑的一句话

为了加快开发过程,应预先考虑程序的逻辑顺序。这是您开发脚本的第一步。

可以使用多种方法;最常见的方法之一是使用列表。列出程序中涉及的任务列表允许您描述每个过程。各个任务可以通过其项目编号引用。

使用您自己的口语来确定程序要执行的任务将帮助您创建程序的可理解形式。稍后,您可以将日常语言语句替换为 shell 语言单词和结构。

下面的示例显示了这样的逻辑流程设计。它描述了日志文件的轮换。此示例显示了一个可能的重复循环,由您要轮换的基础日志文件数量控制

  1. 您要轮换日志吗?

    1. 如果 是

      1. 输入包含要轮换的日志的目录名称。

      2. 输入日志文件的基本名称。

      3. 输入日志应保留的天数。

      4. 在用户的 crontab 文件中进行永久设置。

    2. 如果 否,转到步骤 3。

  2. 您要轮换另一组日志吗?

    1. 如果 是:重复步骤 1。

    2. 如果 否:转到步骤 3。

  3. 退出

用户应提供信息以供程序执行某些操作。必须获取并存储来自用户的输入。应通知用户他们的 crontab 将会更改。

1.5.5. Bash 脚本示例:mysystem.sh

下面的mysystem.sh脚本执行一些众所周知的命令(datewunameuptime)来显示关于您和您的机器的信息。

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 行给出正常运行时间和负载信息。

echoprintf 都是 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 章 中讨论。

NoteBourne Again shell 的标准位置
 

这意味着 bash 程序安装在/bin.

Warning如果 stdout 不可用
 

如果您从 cron 执行脚本,请提供完整路径名并重定向输出和错误。由于 shell 在非交互模式下运行,如果您不考虑这一点,任何错误都会导致脚本过早退出。

以下章节将讨论上述脚本的详细信息。

1.5.6. init 脚本示例

init 脚本在 UNIX 和 Linux 机器上启动系统服务。系统日志守护程序、电源管理守护程序、名称和邮件守护程序是常见的示例。这些脚本,也称为启动脚本,存储在系统上的特定位置,例如/etc/rc.d/init.d/etc/init.d。Init,初始进程,读取其配置文件并决定在每个运行级别中启动或停止哪些服务。运行级别是进程的配置;每个系统都有一个单用户运行级别,例如,用于执行管理任务,为此系统必须尽可能处于未使用状态,例如从备份中恢复关键文件系统。重启和关机运行级别通常也已配置。

启动服务或停止服务时要执行的任务列在启动脚本中。配置 init 是系统管理员的任务之一,以便在正确的时刻启动和停止服务。当面对此任务时,您需要充分了解系统上的启动和关机程序。因此,我们建议您阅读 initinittab的手册页,然后再开始编写自己的初始化脚本。

这是一个非常简单的示例,它将在启动和停止机器时播放声音

#!/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 节 中描述。