mcpField
B
bash

bash

github.com/bentsolheim開発1040 回インストール9 週間前に更新
リポジトリ

bash について

bash は、AI エージェントによるシェルスクリプトの作成、編集、レビュー、リファクタリングを支援するスキルです。単純なスクリプトと複雑なスクリプトを区別し、構成、エラー処理、テストの指針を示します。

主な機能

  1. 01単純・通常スクリプトの指針
  2. 02スクリプト構成のテンプレート
  3. 03引数解析と依存関係チェック
  4. 04明示的なエラー処理と終了コード
  5. 05テストと source の確認

活用シーン

  1. 01デプロイやバックアップ用スクリプトの作成
  2. 02Bash コードのレビューやリファクタリング
  3. 03CI やデータ変換用の小規模スクリプト作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bentsolheim/public-skills --skill bash

インストール後、Claude Code で/bashを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

CLAUDE.md
README.md
scripts/
scaffold.sh
templates/
script-template.sh
simple-script-template.sh

詳細

Bash ベストプラクティススキル

このスキルは、すべての bash スクリプトが保守性、信頼性、使いやすさに関するエンタープライズ品質のベストプラクティスに従うようにします。

このスキルが適用される場合

次の場合に、このスキルが自動的に適用されます。

  • 新しい bash スクリプトまたはシェルスクリプトを作成する
  • 既存の .sh ファイルまたは .bash ファイルを編集する
  • 自動化、デプロイ、バックアップ、またはシステムタスク用のスクリプトをユーザーが求める
  • bash コードをレビューまたはリファクタリングする
  • ユーザーが bash、シェルスクリプト、main 関数、引数解析、スクリプト構成に言及する

スクリプトの複雑さに関するガイドライン

このスキルでは、構造要件が異なるスクリプトを 2 つのカテゴリに分類します。

シンプルなスクリプト

不要な形式にこだわらず、1 つのことをうまく行うスクリプトです。

特徴:

  • 実際のロジックが約 30 行未満(コメントと空白を除く)
  • コマンドライン引数が不要
  • 目的が 1 つに絞られ、見れば明らかである
  • 処理の流れが直線的、または分岐が最小限
  • 出力が他のプログラムやスクリプトで使われることが多い

例: バージョン検出、環境チェック、データ形式の変換、簡単な計算

構造要件:

  • Main 関数とガード節:任意
  • Usage 関数:不要(説明する引数がないため)
  • 引数解析:該当なし
  • 色付き出力:任意(不要な場合が多い)
  • 関数でラップせずに直接実行してよい
一般的なスクリプト

保守性のために完全な構成が必要な、より幅広い用途のスクリプトです。

特徴:

  • ロジックが約 30 行を超える、または
  • コマンドライン引数を受け取る、または
  • 複数の関数や複雑な分岐がある、または
  • 人が対話的に使用する、または
  • 詳細なドキュメントが必要

例: デプロイツール、バックアップユーティリティ、システム保守スクリプト、開発ツール

構造要件:

  • Main 関数とガード節:必須
  • Usage 関数:必須
  • 引数解析:引数を受け取る場合は必須
  • 完全なエラー処理:必須
  • ユーザーフレンドリーな出力:必須(対話型スクリプトの場合)

適用する基本原則

一般的なスクリプトの場合

一般的なスクリプトを作成する際は、次の点を確実に守ってください。

  1. ガード節を備えた Main 関数パターン - すべてのスクリプトに main() 関数を設ける
  2. Usage 関数 - 明確なヘルプドキュメントを用意する
  3. 引数解析 - main 内でオプションを構造的に処理する
  4. 依存関係のチェック - 必要なツールを明示的に検証する
  5. 明示的なエラー処理 - set -e は使用せず、エラーを明示的に処理する
  6. 関数の整理 - 適切な順序と単一責任を守る
  7. ユーザーフレンドリーな出力 - ステータス表示付きの色分けされた構造的な出力にする
シンプルなスクリプトの場合

シンプルなスクリプトを作成する際は、次の点を確実に守ってください。

  1. 目的を明示するコメント - スクリプトの先頭で、何を行うかを説明する
  2. 明示的なエラー処理 - set -e は使用せず、エラーを明示的に処理する
  3. 適切な終了コード - 成功時は 0、失敗時は 0 以外を返す
  4. 標準エラー出力へのエラー表示 - エラーメッセージには >&2 を使用する
  5. 変数の安全性 - 変数を引用符で囲み、未定義の場合に対処する

スクリプト構成テンプレート

一般的なスクリプト用テンプレート

新しい bash スクリプトは、すべて次の構成に従う必要があります。

#!/usr/bin/env bash

# Global declarations
DEPENDENCIES=(jq curl git)  # List external tools needed
SCRIPT_NAME=$(basename "$0")
VERSION="1.0.0"

# Color definitions (if outputting to terminal)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Allow users to disable colors
if [[ -n "${NO_COLOR:-}" ]] || [[ "${TERM:-}" == "dumb" ]]; then
    RED=""
    GREEN=""
    YELLOW=""
    BLUE=""
    NC=""
fi

function usage() {
    cat <<EOM

Brief description of what this script does.

usage: ${SCRIPT_NAME} [options]

options:
    -i|--input        <file>     Input file to process (required)
    -o|--output       <file>     Output file (optional, defaults to stdout)
    -v|--verbose                 Enable verbose output
    -h|--help                    Show this help message
    --version                    Show version information

dependencies: ${DEPENDENCIES[@]}

examples:
    ${SCRIPT_NAME} -i data.txt -o report.json
    ${SCRIPT_NAME} --input data.txt --verbose

EOM
    exit 1
}

function main() {
    # Default values
    local input_file=""
    local output_file=""
    local verbose=false

    # Parse arguments
    while [ "$1" != "" ]; do
        case $1 in
        -i | --input)
            shift
            input_file="$1"
            ;;
        -o | --output)
            shift
            output_file="$1"
            ;;
        -v | --verbose)
            verbose=true
            ;;
        --version)
            echo "${SCRIPT_NAME} version ${VERSION}"
            exit 0
            ;;
        -h | --help)
            usage
            ;;
        *)
            echo "Error: Unknown option '$1'"
            usage
            ;;
        esac
        shift
    done

    # Validate required arguments
    if [ -z "$input_file" ]; then
        echo "Error: Input file is required" >&2
        usage
    fi

    # Check dependencies
    exit_on_missing_tools "${DEPENDENCIES[@]}"

    # Main script logic
    process_file "$input_file" "$output_file" "$verbose"
}

# Business logic functions
function process_file() {
    local input="$1"
    local output="$2"
    local verbose="$3"

    # Implementation here
    echo "Processing ${input}..."
}

# Utility functions
function exit_on_missing_tools() {
    for cmd in "$@"; do
        if command -v "$cmd" &>/dev/null; then
            continue
        fi
        printf "Error: Required tool '%s' is not installed or not in PATH\n" "$cmd"
        exit 1
    done
}

# Guard clause - only execute main if script is run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
    exit 0
fi
シンプルなスクリプト用テンプレート

引数なしで単一の処理を行うスクリプトの場合:

#!/usr/bin/env bash
# Purpose: Determine version for CI builds based on git state
# Usage: determine-version.sh
# Output: Version string to stdout

# For simple scripts, direct execution is fine - no main() required

# Check if we're on a tag
TAG=$(git describe --exact-match --tags HEAD 2>/dev/null)

if [ -n "$TAG" ]; then
    # On a tag - use the tag as version
    echo "${TAG#v}"
else
    # Not on tag - determine from branch
    if [ -n "$GITHUB_HEAD_REF" ]; then
        # Pull request
        BRANCH="$GITHUB_HEAD_REF"
    elif [ -n "$GITHUB_REF_NAME" ]; then
        # Push to branch
        BRANCH="$GITHUB_REF_NAME"
    else
        # Fallback to current branch
        BRANCH=$(git symbolic-ref -q --short HEAD 2>/dev/null)
        if [ -z "$BRANCH" ]; then
            echo "Error: Unable to determine branch" >&2
            exit 1
        fi
    fi

    # Sanitize branch name for version string
    SANITIZED=$(echo "$BRANCH" | sed 's/[^a-zA-Z0-9._-]/_/g')
    echo "${SANITIZED}-SNAPSHOT"
fi

シンプルなスクリプトの主なポイント:

  • 先頭に目的を明示するコメントを記載する
  • main() でラップせずに直接実行する
  • エラーは引き続き >&2 を使って標準エラー出力に表示する
  • 意味のある終了コードを使う(成功時は 0、失敗時は 0 以外)
  • 変数は引き続き適切に引用符で囲む
  • 引数がない場合、usage() 関数は不要
  • 出力がデータの場合、色付き出力は不要

詳細なベストプラクティス

1. スクリプトの初期化
Main 関数パターン
  • エントリーポイントとして、必ず main 関数を使用する
  • これにより、他のスクリプトから安全に source できる
  • 明確な構成とテストのしやすさを実現する
ガード節
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
    exit 0
fi
  • スクリプトの最後に置く必要があります
  • スクリプトを実行せずに読み込めるようにします
  • main の後には必ず明示的に exit 0 を記述します
2. 使用方法関数と引数の解析
使用方法関数の要件
  • main() 関数より前に定義する必要があります
  • 整った書式にするため、ヒアドキュメントを使用します
  • 説明、オプション、依存関係、使用例を含めます
  • ステータス 1 で終了します
引数解析のベストプラクティス
function main() {
    # Set defaults first
    local input_file=""
    local verbose=false

    # Parse with while loop and case
    while [ "$1" != "" ]; do
        case $1 in
        -i | --input)
            shift
            input_file="$1"
            ;;
        -v | --verbose)
            verbose=true
            ;;
        -h | --help)
            usage
            ;;
        *)
            echo "Error: Unknown option '$1'"
            usage
            ;;
        esac
        shift
    done

    # Validate after parsing
    if [ -z "$input_file" ]; then
        echo "Error: Input file is required" >&2
        usage
    fi
}
3. 依存関係の宣言と確認
依存関係は必ず宣言する
DEPENDENCIES=(jq curl git docker)
使用前に確認する
function exit_on_missing_tools() {
    for cmd in "$@"; do
        if command -v "$cmd" &>/dev/null; then
            continue
        fi
        printf "Error: Required tool '%s' is not installed or not in PATH\n" "$cmd"
        exit 1
    done
}
使用方法に記載する

ユーザーが事前に要件を把握できるよう、使用方法関数に依存関係を記載します。

4. 関数の構成
正しい関数の順序(必須)
  1. usage() - グローバル宣言の直後に配置します
  2. main() - usage の直後に配置します
  3. ビジネスロジック関数 - 中核となる機能
  4. ユーティリティ関数 - 汎用ヘルパー

「# UTILITY FUNCTIONS」のようなセクション見出しのコメントは決して追加しないでください。コードの構成だけで十分に伝わります。

関数設計の原則
  • 単一責任:各関数は1つのことだけを行います
  • 明確な入出力:ローカル変数を使い、意味のあるステータスを返します
  • 説明的な名前:動詞と名詞を組み合わせて使います(create_backup、validate_input)
  • 小さく保つ:50行を超える関数は分割します
  • ローカル変数を使う:必ず local で宣言します
5. エラー処理
set -e は使用しない

set -e は決して使用しないでください。代わりに、明示的なエラー処理を行います。

# Good - Explicit handling
if ! command; then
    echo "Error: Command failed" >&2
    exit 1
fi

# Good - Chain with ||
cd /some/directory || {
    echo "Error: Cannot change directory" >&2
    exit 1
}

# Good - Capture and check
output=$(command 2>&1)
if [ $? -ne 0 ]; then
    echo "Error: Command failed: $output" >&2
    exit 1
fi
エラー処理のルール
  • エラーは必ず >&2 で標準エラー出力にリダイレクトします
  • 何が失敗したのか、状況が分かる情報を示します
  • 意味のある終了コードを使います
  • 失敗時には一時ファイルを削除します
6. ユーザーに分かりやすい出力
色付き出力(適切な場合)
# Define colors at script top
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# Respect NO_COLOR environment
if [[ -n "${NO_COLOR:-}" ]] || [[ "${TERM:-}" == "dumb" ]]; then
    RED="" GREEN="" YELLOW="" BLUE="" NC=""
fi
出力関数
function print_header() {
    echo -e "${BLUE}=======================${NC}"
    echo -e "${BLUE}$1${NC}"
    echo -e "${BLUE}=======================${NC}"
}

function print_success() {
    echo -e "${GREEN}✅ $1${NC}"
}

function print_error() {
    echo -e "${RED}❌ Error: $1${NC}" >&2
}

function print_warning() {
    echo -e "${YELLOW}⚠️  Warning: $1${NC}"
}

function print_step() {
    local current="$1"
    local total="$2"
    local message="$3"
    echo -e "${YELLOW}[${current}/${total}] ${message}${NC}"
}
7. main 関数を簡潔に保つ

main で行うのは、次の処理だけです。

  1. 引数を解析する
  2. 入力を検証する
  3. 依存関係を確認する
  4. ビジネス関数を呼び出す
  5. 高レベルのエラーを処理する

ビジネスロジックは別の関数に記述します。

8. コードコメント
コメントの多用を避ける
  • 適切な関数名を付ければ、コード自体で内容が分かります
  • 「何を」ではなく「なぜ」をコメントします
  • 次の場合にコメントを使用します。
    • 複雑なアルゴリズム
    • 一見して分からない回避策
    • ビジネスロジックのルール
    • 外部 API 固有の癖
# Bad - Obvious comment
# Increment counter
counter=$((counter + 1))

# Good - Explains why
# Exponential backoff with jitter to prevent thundering herd
delay=$((2 ** attempt * 1000 + RANDOM % 1000))

スクリプト生成チェックリスト

新しいスクリプトを生成するときは、次の項目を確認します。

  • シバン行:#!/usr/bin/env bash
  • DEPENDENCIES 配列を宣言している
  • usage() 関数を最初に定義している
  • main() 関数で引数を解析している
  • exit_on_missing_tools で依存関係を確認している
  • スクリプトの最後にガード節がある
  • 明示的なエラー処理を行っている(set -e は使用しない)
  • 関数が適切な順序で並んでいる
  • 関数内でローカル変数を使用している
  • エラーを標準エラー出力にリダイレクトしている
  • 意味のある終了コードを使用している
  • (該当する場合)NO_COLOR に対応した色付き出力を使用している

よく使うパターン

対話型プロンプト
# Yes/no confirmation
read -p "Continue? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "Operation cancelled"
    exit 0
fi

# Menu selection
echo "Select an option:"
echo "1) Full backup"
echo "2) Incremental backup"
read -p "Enter choice (1-2): " choice

# Password (hidden)
read -s -p "Enter password: " password
echo  # Add newline
一時ファイル
# Create temp file safely
temp_file=$(mktemp)
trap "rm -f '$temp_file'" EXIT

# Use temp directory
temp_dir=$(mktemp -d)
trap "rm -rf '$temp_dir'" EXIT
パイプラインのエラー処理
# Without set -e, check PIPESTATUS
command1 | command2 | command3
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
    echo "Error: command1 failed" >&2
    exit 1
fi

参考にする例

ユーザーから一般的なタスク用のスクリプトを求められた場合は、次のパターンを適用します。

バックアップスクリプト
  • バックアップ名にタイムスタンプを含めます
  • バックアップ前にソースの存在を確認します
  • 利用可能なディスク容量を確認します
  • 進行状況を知らせます
  • 保持期間に基づいて古いバックアップを削除します
デプロイスクリプト
  • 最初に環境を検証します
  • 各手順の進行状況を明確に示します
  • 失敗時にはロールバックします
  • すべての操作をログに記録します
  • 最後に概要を提示します
データ処理スクリプト
  • 入力形式を検証します
  • 大きなファイルを効率的に処理します
  • 進行状況を示します
  • 失敗時に再開できるようにします
  • 詳細なレポートを生成します

シンプルなスクリプトと通常のスクリプトの選択

判断フロー

どのパターンを使うか判断するには、次の質問に答えてください。

  1. スクリプトはコマンドライン引数を受け取りますか?

    • はい → 通常のスクリプトパターンを使います(usage() と解析が必要です)
    • いいえ → 質問 2 に進みます
  2. ロジックは約 30 行を超えますか(コメントを除く)?

    • はい → 通常のスクリプトパターンを使います(構造が必要です)
    • いいえ → 質問 3 に進みます
  3. 複数の関数や複雑な分岐がありますか?

    • はい → 通常のスクリプトパターンを使います(整理が必要です)
    • いいえ → 質問 4 に進みます
  4. ヘルプを必要とする人が対話的に実行しますか?

    • はい → 通常のスクリプトパターンを使います(ユーザーフレンドリーな機能が必要です)
    • いいえ → 質問 5 に進みます
  5. 単純で、見ればすぐにわかるタスクを 1 つ実行しますか?

    • はい → シンプルなスクリプトパターンを使います
    • いいえ → 通常のスクリプトパターンを使います

迷ったときは、通常のパターンを使ってください。 必要のない構造を用意しておくほうが、必要な構造がないよりも優れています。

シンプルなスクリプトの例
  • determine-version.sh - git の状態に基づいてバージョン文字列を出力します
  • check-env.sh - 環境変数が設定されているか確認します
  • format-json.sh - stdin から stdout へ JSON を再フォーマットします
  • count-lines.sh - 指定されたファイル形式の行数を数えます
  • is-production.sh - production ブランチ上の場合に 0 を返します
通常のスクリプトの例
  • deploy.sh - ロールバックを含む複数ステップのデプロイ
  • backup.sh - 保持期間の管理を伴うデータベースのバックアップ
  • setup-dev.sh - 開発環境の初期化
  • test-runner.sh - 設定可能なテストの実行
  • migrate-data.sh - 検証を伴うデータ移行

スクリプトのテスト

生成したスクリプトは必ず次の項目をテストしてください。

  1. 構文:bash -n script.sh
  2. 未定義の変数:一時的に set -u を設定して実行します
  3. 依存関係:必要なツールをインストールせずに実行します
  4. エラー時の処理:無効な入力でテストします
  5. source の実行:ガード句が機能することを確認します(通常のスクリプトの場合)

覚えておくこと

生成または変更するすべてのスクリプトは、本番環境で使える状態にしてください。

  • 他の人が保守できる
  • 適切な構造によって自己説明的である
  • エラーを適切に処理する
  • 役立つ出力を提供する
  • 一貫したパターンに従う

これらの実践により、スクリプトの品質、信頼性、使いやすさを高められます。

コメント