关于 posix-shell-pro
面向编写和维护 POSIX shell 脚本的技能指南,适用于需要可移植性、安全性和跨类 Unix 环境兼容性的工作。内容涵盖编码规范、验证方法及迁移建议。
主要功能
- 01POSIX 语法与可移植性指导
- 02防御式错误处理与输入校验
- 03跨 shell 测试与静态分析
- 04Bash 到 POSIX sh 的迁移建议
适用场景
- 01编写兼容多种 Unix shell 的脚本
- 02检查脚本中的 Bash 专属语法
- 03为嵌入式环境调整脚本
- 04规划跨 shell 测试与验证
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill posix-shell-pro安装后在 Claude Code 中输入/posix-shell-pro确认已加载。
详细介绍
适用场景
- 处理 posix shell pro 任务或工作流
- 需要 posix shell pro 的指导、最佳实践或检查清单
不适用场景
- 任务与 posix shell pro 无关
- 需要使用超出此范围的其他领域知识或工具
说明
- 明确目标、约束和所需输入。
- 应用相关最佳实践并验证结果。
- 提供可执行的步骤和验证方法。
重点领域
- 严格遵循 POSIX 标准,最大限度提升可移植性
- 编写与 Shell 无关、可在任何类 Unix 系统上运行的脚本
- 采用防御式编程,并使用可移植的错误处理方式
- 不使用 Bash 专属功能,安全地解析参数
- 使用可移植的文件操作和资源管理方式
- 跨平台兼容(Linux、BSD、Solaris、AIX、macOS)
- 使用 dash、ash 和 POSIX 模式进行测试
- 在 POSIX 模式下使用 ShellCheck 进行静态分析
- 仅使用 POSIX 规范定义的功能,保持精简
- 兼容旧版系统和嵌入式环境
可移植性目标:广泛部署的 POSIX.1-2017 Shell,除非用户明确指定 POSIX.1-2024。旧版系统可能不支持较新标准的功能。Shell 符合性与实用程序可用性是两个不同问题;两者都应在目标主机上测试。
POSIX 限制
- 不使用数组(使用位置参数或分隔字符串)
- 不使用
[[条件表达式(仅使用[测试命令) - 不使用进程替换
<()或>() - 不使用大括号展开
{1..10} - 不使用
local关键字(谨慎使用函数作用域变量) - 不使用
declare或typeset;POSIX 支持readonly name=value - 不使用
+=运算符拼接字符串 - 不使用
${var//pattern/replacement}替换 - 不使用关联数组或哈希表
- 不使用
source命令(使用.加载文件)
操作方法
- POSIX Shell 脚本始终使用
#!/bin/shshebang - 可考虑使用
set -eu,但要明确检查预期失败;-e在条件语句和子 Shell 中有例外。pipefail已在 POSIX.1-2024 中标准化,但许多 POSIX.1-2017 时代的 Shell 不支持 - 引用所有变量展开:使用
"$var",绝不使用$var - 所有条件测试均使用
[ ],绝不使用[[ - 使用
while和case解析参数(长选项不使用getopts) - 仅在确认
mktemp存在后才使用它(它不是 POSIX.1-2017 实用程序);否则必须采用经过审核的特定平台安全临时目录方案 - 所有输出均使用
printf,而非echo(echo的行为因实现而异) - 使用
. script.sh加载文件,而非source script.sh - 使用显式的
|| exit 1检查进行错误处理 - 设计脚本时确保操作可重复执行,并支持试运行模式
- 谨慎修改
IFS,并恢复其原始值 - 使用
[ -n "$var" ]和[ -z "$var" ]测试验证输入 - 使用
--结束选项解析,并使用rm -rf -- "$dir"确保安全 - 使用命令替换
$(),而非反引号,以提高可读性 - 使用
date添加时间戳,实现结构化日志记录 - 使用 dash/ash 测试脚本,以验证 POSIX 兼容性
兼容性与可移植性
- 使用
#!/bin/sh调用系统的 POSIX Shell - 在多个 Shell 上测试:dash(Debian/Ubuntu 默认 Shell)、ash(Alpine/BusyBox)、bash --posix
- 避免使用 GNU 专属选项;仅使用 POSIX 规定的标志
- 处理平台差异:使用
uname -s检测操作系统 - 使用
command -v,而非which(可移植性更好) - 检查命令是否可用:
command -v cmd >/dev/null 2>&1 || exit 1 - 为缺失的实用程序提供可移植的实现
- 使用
[ -e "$file" ]检查文件是否存在(适用于所有系统) - 避免使用
/dev/stdin、/dev/stdout(并非所有系统都提供) - 使用显式重定向,而非
&>(Bash 专属)
可读性与可维护性
- 导出的变量使用描述性名称,并采用 UPPER_CASE;局部变量使用 lower_case
- 使用注释块添加章节标题,便于组织内容
- 函数保持在 50 行以内;将复杂逻辑提取出来
- 使用一致的缩进(仅用空格,通常为 2 或 4 个空格)
- 在注释中说明函数用途和参数
- 使用有意义的名称:使用
validate_input,而不是check - 为不明显的 POSIX 变通方案添加注释
- 使用描述性标题将相关函数分组
- 将重复代码提取到函数中
- 使用空行分隔逻辑区块
安全模式
- 为所有变量展开添加引号,以防止单词拆分
- 操作前验证文件权限:
[ -r "$file" ] || exit 1 - 在命令中使用用户输入前先进行清理
- 验证数字输入:
case $num in ''|*[!0-9]*) exit 1 ;; esac - 切勿对不可信输入使用
eval - 使用
--分隔选项与参数:rm -- "$file" - 验证必需变量:
[ -n "$VAR" ] || { echo "VAR required" >&2; exit 1; } - 明确检查退出码:
cmd || { echo "failed" >&2; exit 1; } - 使用
trap进行清理:trap 'rm -f "$tmpfile"' 0; trap 'exit 1' HUP INT TERM - 为敏感文件设置限制性 umask:
umask 077 - 将与安全相关的操作记录到 syslog 或文件中
- 验证文件路径不含意外字符
- 在安全关键脚本中使用命令的完整路径:使用
/bin/rm,而不是rm
性能优化
- 尽可能使用 shell 内置命令,而非外部命令
- 避免在循环中生成子 shell:使用
while read,而不是for i in $(cat) - 将命令结果缓存到变量中,避免重复执行
- 多个字符串比较时使用
case(比反复使用if更快) - 对大型文件逐行处理
- 使用
expr或$(( ))进行算术运算(POSIX 支持$(( ))) - 尽量减少紧密循环中的外部命令调用
- 只需判断真假时使用
grep -q(比捕获输出更快) - 将相似操作批量处理
- 多行字符串使用 here-document,而不是多次调用 echo
文档规范
- 实现
-h帮助标志(避免在未妥善解析的情况下使用--help) - 添加用法说明,展示概要和选项
- 清晰说明必需参数与可选参数
- 列出退出码:0=成功,1=错误;具体故障使用特定代码
- 记录前置条件和必需命令
- 添加说明脚本用途和作者的头部注释
- 提供常见用法示例
- 记录脚本使用的环境变量
- 为常见问题提供故障排除指南
- 在文档中注明 POSIX 合规性
不使用数组
由于 POSIX sh 不支持数组,可使用以下模式:
- 位置参数:
set -- item1 item2 item3; for arg; do echo "$arg"; done - 分隔字符串:优先使用位置参数。如果有意进行拆分,请禁用路径名展开,保存并恢复原始 IFS(包括未设置状态),并定义空字段的处理方式;不要盲目将 IFS 重置为一个空格
- 换行分隔:使用
while IFS= read -r item || [ -n "$item" ]; do printf '%s\n' "$item"; done < "$input_file";带引号的"a\nb"包含字面反斜杠,而非换行符 - 计数器:
i=0; while [ $i -lt 10 ]; do i=$((i+1)); done - 字段拆分:使用
cut、awk或参数展开来拆分字符串
可移植的条件判断
使用 [ ] 测试命令和 POSIX 运算符:
- 文件测试:
[ -e file ]表示文件存在,[ -f file ]表示普通文件,[ -d dir ]表示目录 - 字符串测试:
[ -z "$str" ]表示为空,[ -n "$str" ]表示不为空,[ "$a" = "$b" ]表示相等 - 数值测试:
[ "$a" -eq "$b" ]表示相等,[ "$a" -lt "$b" ]表示小于 - 逻辑运算:
[ cond1 ] && [ cond2 ]表示 AND,[ cond1 ] || [ cond2 ]表示 OR - 取反:
[ ! -f file ]表示不是文件 - 模式匹配:使用
case,而不是[[ =~ ]]
CI/CD 集成
- 矩阵测试:在 Linux、macOS 和 Alpine 上使用 dash、ash、bash --posix、yash 进行测试
- 容器测试:固定经过审核的 Alpine/Debian 镜像摘要,以确保 ash/dash 测试可复现;浮动标签会变化
- 提交前钩子:配置 checkbashisms、shellcheck -s sh、shfmt -ln posix
- GitHub Actions:使用 shellcheck-problem-matchers 并启用 POSIX 模式
- 跨平台验证:在 Linux、macOS、FreeBSD、NetBSD 上进行测试
- BusyBox 测试:在嵌入式系统的 BusyBox 环境中验证
- 自动化发布:标记版本并生成可移植的分发包
- 覆盖率跟踪:确保所有 POSIX shell 的测试覆盖率
- 示例工作流:
shellcheck -s sh *.sh && shfmt -ln posix -d *.sh && checkbashisms *.sh
嵌入式系统与受限环境
- BusyBox 兼容性:使用 BusyBox 功能受限的 ash 实现进行测试
- Alpine Linux:默认 shell 是 BusyBox ash,而非 bash
- 资源限制:尽量减少内存用量,避免启动过多进程
- 缺少实用工具:常用工具不可用时提供备用实现(
mktemp、seq) - 只读文件系统:处理
/tmp可能受限的情况 - 没有 coreutils:有些环境缺少 GNU coreutils 扩展
- 信号处理:极简环境中的信号支持有限
- 启动脚本:init 脚本必须符合 POSIX 标准,以实现最大兼容性
- 示例:检查 mktemp:
command -v mktemp >/dev/null 2>&1 || mktemp() { ... }
从 Bash 迁移到 POSIX sh
- 评估:运行
checkbashisms,找出 Bash 特有语法 - 移除数组:将数组转换为分隔字符串或位置参数
- 更新条件判断:将
[[替换为[,并将正则表达式改为case模式 - 局部变量:移除
local关键字,改用函数前缀 - 进程替换:将
<()替换为临时文件或管道 - 参数展开:使用
sed/awk进行复杂的字符串处理 - 测试策略:逐步转换并持续验证
- 文档:注明任何 POSIX 限制或变通方法
- 渐进式迁移:每次转换一个函数,并充分测试
- 备用支持:如有需要,在迁移期间保留两种实现
质量检查清单
- 脚本通过带有
-s sh标志的 ShellCheck 检查(POSIX 模式) - 使用 shfmt 的
-ln posix选项统一格式化代码 - 在多种 shell 中测试:dash、ash、bash --posix、yash
- 所有变量展开都正确加引号
- 不使用 Bash 特有功能(数组、
[[、local等) - 错误处理涵盖所有失败情况
- 通过 EXIT trap 清理临时资源
- 脚本提供清晰的使用说明
- 输入验证可防止注入攻击
- 脚本可在类 Unix 系统(Linux、BSD、Solaris、macOS、Alpine)上移植使用
- 已验证适用于嵌入式用例的 BusyBox 兼容性
- 不使用 GNU 特有的扩展或标志
输出
- 符合 POSIX 标准、可移植性最大化的 shell 脚本
- 使用 shellspec 或 bats-core 编写的测试套件,可在 dash、ash、yash 上验证
- 用于多 shell 矩阵测试的 CI/CD 配置
- 带有备用实现的常用模式可移植实现
- 关于 POSIX 限制和变通方法的文档及示例
- 将 Bash 脚本逐步转换为 POSIX sh 的迁移指南
- 跨平台兼容性矩阵(Linux、BSD、macOS、Solaris、Alpine)
- 比较不同 POSIX shell 的性能基准
- 缺少实用工具(mktemp、seq、timeout)的备用实现
- 适用于嵌入式和容器环境的 BusyBox 兼容脚本
- 无需依赖 bash、适用于各种平台的软件包分发
必备工具
静态分析与格式化
- ShellCheck:静态分析器,使用
-s sh进行 POSIX 模式验证 - shfmt:Shell 格式化工具,使用
-ln posix选项处理 POSIX 语法 - checkbashisms:检测脚本中的 Bash 专属结构(来自 devscripts)
- Semgrep:使用 POSIX 专属安全规则进行 SAST
- Shell 分析覆盖范围:确认分析器确实支持 Shell;默认不要声称 CodeQL 支持 Shell
用于测试的 POSIX Shell 实现
- dash:Debian Almquist Shell——轻量且严格遵循 POSIX(主要测试目标)
- ash:Almquist Shell——BusyBox 默认 Shell,用于嵌入式系统
- yash:Yet Another Shell——用于验证严格的 POSIX 一致性
- posh:Policy-compliant Ordinary Shell——符合 Debian 策略
- osh:Oil Shell——现代 POSIX 兼容 Shell,错误消息更完善
- bash --posix:处于 POSIX 模式的 GNU Bash,用于兼容性测试
测试框架
- bats-core:Bash 测试框架(可与 POSIX sh 配合使用)
- shellspec:支持 POSIX sh 的 BDD 风格测试框架
- shunit2:支持 POSIX sh 的 xUnit 风格框架
- sharness:Git 使用的测试框架(兼容 POSIX)
应避免的常见陷阱
- 使用
[[而不是[(Bash 专属) - 使用数组(POSIX sh 不支持)
- 使用
local关键字(Bash/ksh 扩展) - 不使用
printf而使用echo(不同实现的行为各异) - 使用
source而不是.来引入脚本 - 使用 Bash 专属的参数展开:
${var//pattern/replacement} - 使用进程替换
<()或>() - 使用
function关键字(ksh/Bash 语法) - 使用
$RANDOM变量(不属于 POSIX) - 使用
read -a处理数组(Bash 专属) - 假设旧版 POSIX.1-2017 Shell 支持
set -o pipefail - 使用
&>重定向(应使用>file 2>&1)
高级技巧
- 退出时清理:使用
trap 'cleanup' 0;如果清理可能失败,请保留原始退出状态。$LINENO和始终打印错误的退出陷阱并非可移植的错误检测方式 - 安全的临时文件:
tmpfile=$(mktemp) || exit 1; trap 'rm -f "$tmpfile"' EXIT INT TERM - 模拟数组:
set -- item1 item2 item3; for arg; do process "$arg"; done - 字段解析:
IFS=:; while read -r user pass uid gid; do ...; done < /etc/passwd - 字符串替换:
echo "$str" | sed 's/old/new/g',或使用参数展开${str%suffix} - 默认值:若 var 未设置或为空,
value=${var:-default}会赋予默认值 - 可移植的函数:避免使用
function关键字,改用func_name() { ... } - 子 Shell 隔离:
(cd dir && cmd)可切换目录,而不影响父 Shell - Here 文档:使用带引号的
cat <<'EOF'可阻止变量展开 - 检查命令是否存在:
command -v cmd >/dev/null 2>&1 && echo "found" || echo "missing"
POSIX 专属最佳实践
- 始终为变量展开加引号:使用
"$var",而不是$var - 使用带正确空格的
[ ]:使用[ "$a" = "$b" ],而不是["$a"="$b"] - 字符串比较使用
=,而不是==(Bash 扩展) - 使用
.引入脚本,而不是source - 所有输出都使用
printf,避免使用echo -e或echo -n - 使用
$(( ))进行算术运算,而不是let或declare -i - 使用
case进行模式匹配,而不是[[ =~ ]] - 使用
sh -n script.sh检查脚本语法 - 为保证可移植性,使用
command -v,而不是type或which - 使用
|| exit 1显式处理所有错误情况
参考资料与延伸阅读
POSIX 标准与规范
- POSIX Shell 命令语言 - 此 URL 上的官方 POSIX.1-2017 规范
- POSIX 实用工具 - POSIX 规定的实用工具完整列表
- Autoconf 可移植 Shell 编程 - GNU 提供的全面可移植性指南
可移植性与最佳实践
- Rich 的 sh(POSIX shell)技巧 - 高级 POSIX shell 技术
- Suckless Shell 风格指南 - 极简 POSIX sh 模式
- FreeBSD Porter's Handbook - Shell - BSD 可移植性注意事项
工具与测试
- checkbashisms - 检测 bash 专属结构
示例
用户请求:
在此任务中使用 @posix-shell-pro:精通严格的 POSIX sh 脚本编写,确保在类 Unix 系统间具有最大可移植性。
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。
评论
支持 Markdown