11月6日、Nicholas C. ZakasがESLint公式ブログに「ESLint now officially supports linting of shell scripts」と題した記事を公開した。JavaScriptプロジェクトに混在するシェルスクリプトを、既存の eslint.config.js にプラグインを追加するだけで同一設定ファイルからまとめてリントできる新プラグイン @eslint/shell のリリースを紹介している。
ESLintといえばJavaScript向けの静的解析ツールというイメージが強いが、2024年7月に発表された「言語非依存の汎用リンタープラットフォーム化」構想のもと、JSON、Markdown、CSSと順次サポートを拡大してきた。今回のシェルスクリプト対応はその延長線上にある。多くのJavaScript/TypeScriptプロジェクトにはビルドや自動化のためのシェルスクリプトが混在しているが、これまではJavaScriptはESLintで管理できても、シェルスクリプトだけShellCheckを別途導入するか放置するかという状況だった。@eslint/shell はShellCheckの置き換えではなく、ESLintエコシステムへの統合という位置づけだ。CIのセットアップを増やさずに、シェルスクリプトも統一された設定で管理できる点が最大のメリットである。
@eslint/shell ——インストールして設定に追加するだけ
新しい @eslint/shell プラグインをインストールするだけで、ESLintの既存の設定システムからシェルスクリプトをリントできる。
npm install @eslint/shell -D
eslint.config.js にrecommended設定を追加するだけで使い始められる:
import { defineConfig } from "eslint/config";
import shell from "@eslint/shell";
export default defineConfig([
{
plugins: { shell },
extends: ["shell/recommended"],
},
]);
recommended設定はBashを対象とし、以下のような問題を検出するルールが有効化される:
- クォートされていない変数展開(unquoted expansions)
- 未使用変数
- 安全でない
readの使用
どんな問題を検出できるか
対応するシェルの方言(ダイアレクト)は Bash、POSIX sh、MirBSD Korn shell(mksh) の3種類だ。ダイアレクトとは、シェルスクリプトの書き方の違いを指す——Bashだけで動く構文もあれば、どのPOSIX互換シェルでも動く書き方もあり、スクリプトの移植性に関わる重要な区別である。
ルールはシェルスクリプト向けの定番静的解析ツールShellCheckからインスパイアされており、以下のような問題を検出する:
- バッククォートによるレガシーなコマンド置換(
`cmd`→$(cmd)を推奨) lsの出力をループで処理するパターン(パース問題の温床として知られる)printfフォーマット文字列中の変数使用
ダイアレクトを明示して個別にルールを設定することも可能だ:
import { defineConfig } from "eslint/config";
import shell from "@eslint/shell";
export default defineConfig([
{
files: ["**/*.sh"],
plugins: { shell },
language: "shell/posix",
rules: {
"shell/no-backticks": "error",
"shell/no-unquoted-expansions": "error",
},
},
]);
カスタムルールも書ける
このプラグインの設計上の特徴として、シェルスクリプトをESTree形式の構文木(AST)にパースする点が挙げられる。JavaScriptのルールを書くのと同じ感覚でカスタムルールを実装できる。
パーサーの実装には mvdan-sh を採用している。これはシェルスクリプトのフォーマッターである shfmt が使用しているパーサーで、実績のある実装だ。構文木の確認には ESLint Code Explorer が使える。
詳細はESLint now officially supports linting of shell scriptsを参照していただきたい。