mcpField
P
posix-shell-pro

posix-shell-pro

github.com/sickn33开发48.7k241 次安装今天更新
代码仓库

关于 posix-shell-pro

面向编写和维护 POSIX shell 脚本的技能指南,适用于需要可移植性、安全性和跨类 Unix 环境兼容性的工作。内容涵盖编码规范、验证方法及迁移建议。

主要功能

  1. 01POSIX 语法与可移植性指导
  2. 02防御式错误处理与输入校验
  3. 03跨 shell 测试与静态分析
  4. 04Bash 到 POSIX sh 的迁移建议

适用场景

  1. 01编写兼容多种 Unix shell 的脚本
  2. 02检查脚本中的 Bash 专属语法
  3. 03为嵌入式环境调整脚本
  4. 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/sh shebang
  • 可考虑使用 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-pro:精通严格的 POSIX sh 脚本编写,确保在类 Unix 系统间具有最大可移植性。

限制

  • 仅当任务明确符合上述范围时,才使用此技能。
  • 不要将输出视为环境特定验证、测试或专家审查的替代品。
  • 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。

评论

支持 Markdown