LabHub
学习 学习路径 课程

Shell 脚本

写出健壮的脚本

在 LabHub 中继续学习

目标

逐一实现可以部署到生产服务器的脚本应具备的条件:不隐藏失败、保证收尾清理,并且重复执行也安全。

为什么重要

shell 默认非常宽容:不存在的变量会被当作空字符串,管道中间的失败会在最后一条命令成功时被掩盖,脚本即使异常退出也会留下临时文件。这种宽容会造成‘每天都报告成功的备份其实是 0 字节’之类的事故。set -euo pipefail 用于在发现问题的位置立即停止trap ... EXIT 则保证无论通过哪条路径结束都会执行清理。二者配合使用才有意义。

步骤

  1. 创建 /root/robust/strict.sh。在开头加入 set -euo pipefail,接收一个参数并原样输出。strict.sh hello 应输出 hello 并以 0 结束;不带参数运行时必须以非 0 退出码结束
  2. 创建 /root/robust/pipe.sh。参数为 ok 时运行成功的管道,参数为 broken 时运行前一阶段失败的管道。broken 时退出码必须非 0。
  3. 创建 /root/robust/tmpwork.sh。用 mktemp 创建临时文件,将路径输出到标准输出的一行中,并在退出时通过 trap 确保删除。传入参数 fail 时要以失败结束,但此时也不能留下临时文件。
  4. 创建 /root/robust/once.sh。运行后工作约 3 秒并以 0 结束。如果已有实例正在运行,再次执行时应提示 already running,并以退出码 9 结束。前一个实例结束后必须能再次正常运行。
  5. 创建 /root/robust/count.sh <디렉터리>。只输出该目录直属的普通文件数量。目录路径和文件名中即使有空格也必须正确;没有参数时必须以非 0 退出码结束。
  6. 创建 /root/robust/backup.sh <원본디렉터리> <결과파일>
    • 没有参数时,向标准错误输出包含 usage 的提示,并以退出码 64 结束
    • 原路径不存在时,以退出码 66 结束
    • 正常时,创建包含子目录的 tar.gz 文件,并以退出码 0 结束
  7. 创建 /root/robust/retry.sh <횟수> <명령...>。命令失败时最多重试指定次数,每次尝试之间至少等待 1 秒,成功后立即停止。每次尝试都要留下包含 attempt 一词的日志。
  8. 创建 /root/robust/audit.sh <스크립트경로>。检查目标脚本是否包含 shebang 和严格模式声明,输出 strict=yes|noshebang=yes|no 两行。两者均为 yes 时退出码为 0,否则为非 0。文件不存在时也必须返回非 0。

提示

启用严格模式

创建 /root/robust/strict.sh。在开头加入 set -euo pipefail,接收一个参数并原样输出。strict.sh hello 应输出 hello 并以 0 结束;不带参数运行时必须以非 0 退出码结束

只需一行 set -euo pipefail。启用 -u 后,一旦读取不存在的 $1,脚本就应立即终止。

传播管道失败

创建 /root/robust/pipe.sh。参数为 ok 时运行成功的管道,参数为 broken 时运行前一阶段失败的管道。broken 时退出码必须非 0。

默认 shell 只看最后一条命令的状态。请确保前一阶段失败时,整个脚本也以失败结束。

确保删除临时文件

创建 /root/robust/tmpwork.sh。用 mktemp 创建临时文件,将路径输出到标准输出的一行中,并在退出时通过 trap 确保删除。传入参数 fail 时要以失败结束,但此时也不能留下临时文件。

trap ... EXIT 在正常和错误退出时都会执行。请用 mktemp 创建临时文件。

防止重复运行

创建 /root/robust/once.sh。运行后工作约 3 秒并以 0 结束。如果已有实例正在运行,再次执行时应提示 already running,并以退出码 9 结束。前一个实例结束后必须能再次正常运行。

使用 flock 或基于 mkdir 的锁。第二次运行必须立即以退出码 9 结束,前一个实例结束后锁必须释放。

安全处理含空格的路径

创建 /root/robust/count.sh <디렉터리>。只输出该目录直属的普通文件数量。目录路径和文件名中即使有空格也必须正确;没有参数时必须以非 0 退出码结束。

路径和文件名中都有空格。如果不引用变量,计数就会出错。

用法与退出码约定

创建 /root/robust/backup.sh <원본디렉터리> <결과파일>

用法提示写入标准错误,不同情况返回不同退出码。64 表示用法错误,66 表示输入不存在。

重试辅助脚本

创建 /root/robust/retry.sh <횟수> <명령...>。命令失败时最多重试指定次数,每次尝试之间至少等待 1 秒,成功后立即停止。每次尝试都要留下包含 attempt 一词的日志。

要原样重新执行命令,需要使用 "$@"。成功后立即停止,并且每次尝试之间要等待。

脚本审计工具

创建 /root/robust/audit.sh <스크립트경로>。检查目标脚本是否包含 shebang 和严格模式声明,输出 strict=yes|noshebang=yes|no 两行。两者均为 yes 时退出码为 0,否则为非 0。文件不存在时也必须返回非 0。

读取另一个脚本文件,判断是否含有 shebang 和严格模式。还必须处理文件不存在的情况。