关于 linux-bash-scripting
一项用于编写和验证面向 Linux 上 GNU Bash 4.4+ 的 Bash 脚本的技能。它为开发者提供输入安全、文件操作和性能方面的实践,并要求在完成前运行 ShellCheck 与 shfmt 检查。
主要功能
- 01面向 Linux 上的 GNU Bash 4.4+
- 02规范输入与进程安全处理
- 03涵盖文件、密钥和清理
- 04使用 ShellCheck 与 shfmt 验证
- 05提供语法检查备用方案
适用场景
- 01编写 Linux 自动化脚本
- 02处理不可信输入与子进程
- 03交付前检查脚本
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ iliaal/ ai-skills --skill linux-bash-scripting安装后在 Claude Code 中输入/linux-bash-scripting确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
SPEC.mdreferences/input-and-process-safety.mdproduction-patterns.md详细介绍
Linux Bash 脚本
编写 Bash 脚本,使其通过 shellcheck --enable=all 和 shfmt -d 检查且没有任何警告。
目标环境:Linux 上的 GNU Bash 4.4+。不提供 macOS/BSD 兼容处理,不使用 Windows 路径,也不受 POSIX 专属限制。
脚本基础
#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit
SCRIPT_DIR=$(dirname -- "${BASH_SOURCE[0]}") || exit "$?"
SCRIPT_DIR=$(cd -- "${SCRIPT_DIR}" && pwd -P) || exit "$?"
readonly SCRIPT_DIR
trap 'printf "Error at %s:%d\n" "${BASH_SOURCE[0]}" "$LINENO" >&2' ERR
trap 'rm -rf -- "${_tmpdir:-}"' EXIT
-E会将 ERR 陷阱传播到函数中inherit_errexit会将 errexit 传播到$()命令替换中- 根据
SCRIPT_DIR解析脚本自身的数据文件,绝不依赖调用者的当前工作目录或git rev-parse --show-toplevel。从其他项目的 Git hook、cron 作业或包装脚本调用共享的代码检查器时,当前工作目录属于其他项目,因此调用者相对的规则路径会指向不存在的文件:规则集为空,未发现违规项,退出状态为 0。这是静默无操作,并非错误;而且在其自身仓库中运行时,会因为错误的原因而通过。请从脚本自身目录树以外的临时目录运行一次 - 始终在 EXIT 陷阱中创建临时目录:
_tmpdir=$(mktemp -d) - 将主体放在
main函数中。保护其调用逻辑,使直接执行时运行该函数,而 source 时仅定义函数以供测试。
核心规则
- 为展开内容加引号,使用数组表示命令,绝不将外部数据作为 Shell 代码执行。
- 在算术运算前,验证数字语法、正负号和应用范围。对无符号数字先使用
10#转换,再应用正负号;10#-08无效。 - 不要将密钥放入进程参数或跟踪输出中。通过 stdin 传入,并使用支持 JSON 的编码器。
- 在“缺失”和“检查失败”含义不同时,检查准确的退出状态。将
local和readonly声明与命令替换分开。 - 使用 NUL 分隔的文件迭代,验证必需的标志值,并拒绝互相冲突的输出/目标组合。
- 原子替换时,在目标文件旁暂存;激活多个文件时,切换单个已暂存的发布引用。
- 保留无关文件,并在清理后报告实际的信号或命令状态。
- 不要假设 Bash 选项在
sh中也有效,不要假设 GNU 工具模式的行为等同于系统调用模式,也不要假设已配置的回退路径可用。
特定任务参考资料
实施相应行为前,请阅读相关参考资料:
- 对于命令执行、外部输入、数字转换、参数解析、迭代或子进程状态处理:input-and-process-safety.md。
- 对于文件激活、密钥、锁、重试、清理、权限、日志记录或可重启自动化:production-patterns.md。
性能
- 对外部程序使用参数展开:
${path%/*}代替dirname,${path##*/}代替basename,${var//old/new}代替sed - 使用
(( ))代替expr;使用[[ =~ ]]代替echo | grep - 缓存结果:
val=$(cmd)执行一次,重复使用$val - 使用
xargs -0 -P "$(nproc)"并行处理 - 使用
declare -A map进行查找,代替反复调用 grep
Bash 4.4+ / 5.x
${var@Q}会添加 Shell 引号(Bash 4.4+);${var@U}转为大写,${var@L}转为小写(Bash 5.1+)declare -n ref=varname使用 nameref 进行间接访问wait -n等待任意一个后台作业$EPOCHSECONDS、$EPOCHREALTIME:无需派生date即可获取时间戳(Bash 5.0+)
Linux 专属
- GNU coreutils 与 macOS 不同:
sed -i(不带''后缀)、grep -P(支持 PCRE)、readlink -f(规范路径) - 使用
timeout 30s cmd防止自动化任务挂起 sed -i会将符号链接替换为普通文件:它会写入临时文件并将其重命名到原路径,因此之后两份文件会分开变化,且不会报错。请使用sed -i --follow-symlinks或按名称编辑目标文件,并在脚本化编辑后检查git status --short是否出现T(类型更改)
ShellCheck
运行 shellcheck --enable=all script.sh。重要规则:
- SC2155:将声明与赋值分开
- SC2086:为变量添加双引号
- SC2046:为命令替换添加引号
- SC2164:使用
cd dir || exit - SC2327/SC2328:确保命令替换仍可访问标准输出;将其重定向到其他位置会导致捕获的值为空
- 从
${BASH_REMATCH[n]}读取正则表达式捕获结果,而不是从位置参数读取
提交前检查:shellcheck *.sh && shfmt -i 2 -ci -d *.sh
验证
在声明完成之前,运行 shellcheck --enable=all 和 shfmt -d,确保没有警告。测试边缘情况:空输入、缺失文件、路径中包含空格。
如果未安装 shellcheck 或 shfmt(command -v shellcheck 失败),则表示检查已跳过,而不是通过:报告“未运行静态分析:shellcheck 不可用”,并回退到仅用于语法检查的 bash -n。跳过的检查器与上方的空规则文件一样,都是悄无声息地不执行任何操作。
评论
支持 Markdown