用途
shfmt 用于格式化 shell 脚本,提升可读性和一致性。它解析脚本为抽象语法树(AST),再以规范格式打印,从而消除风格争论,确保代码库统一。
支持的 Shell 方言
shfmt 支持多种 shell 方言,语法规则各异:
- POSIX Shell:最可移植,适用于任何 Unix 系统的 /bin/sh。不支持数组、[[ ]] 测试,$() 必须在所有上下文中可用。
- Bash:脚本中最常用,支持扩展特性,如数组、[[ ]]、算术运算等。
- mksh (MirBSD Korn Shell):Korn shell 变体,有自己的扩展,如关联数组。
- Bats (Bash Automated Testing System):用于 Bats 测试文件。
主要能力
格式化模式
- 缩进:统一脚本中的缩进,可通过 -i 指定空格数(如 -i 2 为两空格,-i 0 为制表符)。
- 空格:规范化操作符和关键字周围的空格。
- 分号与换行:将多个语句拆分为独立行,移除行尾多余分号。
- Here Documents:保留 here-doc 内容,但规范化缩进;<<- 允许剥离制表符。
- 函数定义:统一函数格式,-fn 选项可将大括号置于新行。
- Case 语句:统一 case 格式,-ci 选项可缩进 case 分支。
- 二元操作符:默认将 && 等置于行尾,-bn 选项置于行首。
- 重定向:默认无空格,-sr 选项在重定向符号后添加空格。
常见格式化问题
- 混合制表符与空格:使用 shfmt -i 2 -w 统一为空格,或 -i 0 统一为制表符。
- 行尾分号:shfmt 自动移除。
- 引号不一致:shfmt 保留引号风格,但规范化不必要的引号。
- 长行:shfmt 不自动换行,需手动使用续行符。
- 数组格式:按书写方式保留单行或多行格式。
编辑器集成
- VS Code:安装 "shell-format" 扩展,配置 shellformat.path 和 flag。
- Vim/Neovim:使用 ALE 或原生格式化,设置 formatprg。
- Emacs:使用 reformatter 包定义 shfmt 命令。
- JetBrains IDE:安装 "Shell Script" 插件,在设置中配置 shfmt 路径和选项。
检查与 Diff 模式
- 检查格式(CI 模式):shfmt -d 显示差异(需更改时退出码 1),shfmt -l 列出需格式化的文件。
- 原地格式化:shfmt -w 覆盖文件。
- 预览更改:shfmt 输出到 stdout,或重定向到文件。
Git 集成
- 格式化暂存文件:通过 git diff --cached 获取暂存 .sh 文件并运行 shfmt -w。
- Pre-commit 钩子:检查暂存文件格式,若需格式化则阻止提交。
- 格式化变更文件:格式化自 main 分支以来更改的文件。
压缩(高级)
shfmt -mn 可移除空白和注释,压缩脚本,仅用于分发,不用于开发。
使用方式
基本命令:
- 格式化并输出到 stdout:
shfmt script.sh - 原地格式化:
shfmt -w script.sh - 检查差异:
shfmt -d script.sh - 列出需格式化文件:
shfmt -l . - 指定缩进:
shfmt -i 2 -w script.sh - 强制方言:
shfmt -ln bash script.sh
适用场景
- 统一 shell 脚本格式,确保团队一致性。
- 集成到开发工作流,如保存时自动格式化。
- 在 CI/CD 中验证格式。
- 解决脚本中的格式问题。
- 配置编辑器保存时格式化。
限制
- shfmt 不自动换行长行,需手动续行。
- 不支持 # shfmt:ignore 注释,需通过文件排除来保留特定格式。
- 压缩模式移除注释和空白,仅适用于分发。
- 需确保 shell 方言与脚本 shebang 匹配,否则可能解析错误。
- 格式化后应审查更改,确保符合预期。
最佳实践
- 保存时格式化:配置编辑器自动格式化。
- CI 验证:在流水线中运行 shfmt -d。
- 团队统一设置:提交 .shfmt.toml 到仓库。
- 匹配 Shebang:确保方言与脚本 shebang 一致。
- 格式化后审查:验证更改合理。
- 不混用风格:所有脚本使用相同设置。
- 使用 Pre-commit 钩子:防止未格式化代码提交。
故障排除
- 解析错误:先用 bash -n 或 sh -n 检查语法。
- 方言检测错误:使用 -ln 强制指定方言。
- 保留有意格式:将代码移至不被 shfmt 处理的文件,或接受格式化版本。