linux-bash-scripting について
Linux 上の GNU Bash 4.4+ を対象とする Bash スクリプトの作成・検証スキルです。入力の安全性、ファイル操作、性能に関する実践方法を示し、完了前に ShellCheck と shfmt で確認します。
主な機能
- 01Linux 上の GNU Bash 4.4+ が対象
- 02入力とプロセスの安全な処理
- 03ファイル、シークレット、クリーンアップに対応
- 04ShellCheck と shfmt による検証
- 05構文チェックの代替手順を提示
活用シーン
- 01Linux 自動化スクリプトの作成
- 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 スクリプト
shellcheck --enable=all と shfmt -d で警告が一切出ない Bash スクリプトを作成します。
対象: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 を$()のコマンド置換内にも引き継ぎます- スクリプト自身のデータファイルは、呼び出し元の作業ディレクトリや
git rev-parse --show-toplevelではなく、SCRIPT_DIRを基準に解決してください。別のプロジェクトの Git フック、cron ジョブ、ラッパーから呼び出される共有リンターは、他者の作業ディレクトリで実行されるため、呼び出し元を基準にしたルールのパスは存在しないファイルを指します。その結果、ルールセットが空のまま読み込まれ、違反が 0 件と判定されて終了コード 0 になります。これはエラーではなく、何もせずに終了する状態です。さらに、自身のリポジトリ内から実行すると、間違った理由でテストに合格します。スクリプト自身のツリーではないスクラッチディレクトリから一度実行して確認してください - 一時ディレクトリは必ず EXIT トラップ内で作成します:
_tmpdir=$(mktemp -d) - 本体は
main関数で囲みます。直接実行したときは関数を呼び出し、ソースとして読み込んだときはテスト用の関数定義のみを行うように、呼び出しをガードしてください。
基本ルール
- 展開は引用符で囲み、コマンドには配列を使い、外部データをシェルコードとして評価しないでください。
- 算術演算の前に、数値の構文、符号、アプリケーション上の範囲を検証してください。符号を適用する前に、符号なしの数字を
10#で変換してください。10#-08は無効です。 - シークレットをプロセスの引数やトレースに含めないでください。標準入力を通じて渡し、JSON 対応のエンコーダーを使用してください。
- 「存在しない」と「検査に失敗した」の区別が必要な場合は、終了ステータスを正確に確認してください。
localとreadonlyの宣言はコマンド置換と分けてください。 - NUL 区切りのファイル反復処理を使い、必須のフラグ値を検証し、競合する出力先/対象の組み合わせは拒否してください。
- アトミックに置き換える場合は、置き換え先と同じ場所にステージングしてください。複数ファイルを有効化する場合は、単一のステージング済みリリース参照を切り替えてください。
- 無関係なファイルを保持し、クリーンアップ後に実際のシグナルまたはコマンドのステータスを報告してください。
shで Bash のオプションが動作する、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)"を使います - 繰り返し
grepする代わりに、検索にはdeclare -A mapを使います
Bash 4.4 以降/5.x
${var@Q}はシェル用の引用符付き表記(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はシンボリックリンクを通常ファイルに置き換えます。一時ファイルを書き込み、パス上のファイルと置き換えるため、後で 2 つのコピーの内容が異なってもエラーは発生しません。sed -i --follow-symlinksを使うか、対象ファイルを名前で指定して編集し、スクリプトによる編集後はgit status --shortでT(typechange)がないか確認してください
ShellCheck
shellcheck --enable=all script.shを実行します。主なルール:
- SC2155:宣言と代入を分けます
- SC2086:変数をダブルクォートで囲みます
- SC2046:コマンド置換を引用符で囲みます
- SC2164:
cd dir || exit - SC2327/SC2328:コマンド置換で標準出力を利用できる状態にします。標準出力をリダイレクトすると、取得される値が空になります
- 正規表現のキャプチャは、位置パラメーターではなく
${BASH_REMATCH[n]}から読み取ります
Pre-commit:shellcheck *.sh && shfmt -i 2 -ci -d *.sh
検証
完了を宣言する前に、警告がゼロになるよう shellcheck --enable=allとshfmt -dを実行します。エッジケース(入力が空、ファイルがない、パスにスペースが含まれる)をテストします。
shellcheckまたはshfmtがインストールされていない(command -v shellcheckが失敗する)場合、チェックは合格ではなくスキップされたものとして扱います。「static analysis not run: shellcheck unavailable」と報告し、構文チェックのみを行うbash -nに切り替えます。リンターをスキップするのは、前述の空のルールファイルと同じく、何も実行されないのと同じです。