Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

実践編:関数とシェルスクリプト設計

シバンの選択、関数の定義とローカルスコープの保護、getopts によるコマンドライン引数の解析、および堅牢なスクリプト全体の構成設計を解説します。

シバン(Shebang)の正しい記述

シェルスクリプトの1行目には、スクリプトを実行するインタープリタを指定する「シバン(Shebang)」を記述します。

#!/usr/bin/env bash

» シバンの選択指針

  • 推奨: #!/usr/bin/env bash: 環境変数 PATH から bash 実行ファイルを探して起動します。OS ごとにインストール先(/bin/bash や /usr/local/bin/bash 等)が異なっていても移植性を維持できます。
  • 避けるべき: #!/bin/sh: sh は Ubuntu 等では dash(機能が限定された軽量シェル)にリンクされています。[[ ... ]] や配列などの Bash 固有構文(Bashism)が含まれていると構文エラーで失敗します。

関数の定義とローカル変数

再利用可能な処理を関数として整理します。

» 関数の定義方法

# POSIX 互換のシンプルな構文
backup_file() {
    local target="$1"
    local dest_dir="${2:-./backup}"

    if [[ ! -f "$target" ]]; then
        echo "Error: File $target not found." >&2
        return 1
    fi

    mkdir -p "$dest_dir"
    cp "$target" "$dest_dir/"
    return 0
}

# 関数の呼び出し
backup_file "config.json" "/var/backup"

» local 宣言によるスコープ保護

関数内で宣言する変数は、必ず local を先頭に付与します。local を省略するとグローバル変数として扱われ、呼び出し元の同名変数を意図せず上書きしてしまう原因になります。

» 関数の戻り値と出力

  • return <数字>: 0〜255 の終了ステータス(成功か失敗か)のみを返します。
  • 計算結果や文字列データを返したい場合は、関数内で echo や printf で標準出力に出力し、呼び出し側がコマンド置換 $(func) で受け取ります。
get_full_name() {
    local first="$1"
    local last="$2"
    printf "%s %s\n" "$last" "$first"
}

NAME=$(get_full_name "Taro" "Yamada")

引数のパース(getopts)

スクリプトに渡された -v(フラグ)や -f file.txt(値付きオプション)などの引数を標準的な手法で解析するには組み込みの getopts を使用します。

#!/usr/bin/env bash

VERBOSE=false
OUTPUT_FILE=""

while getopts ":vf:h" opt; do
    case "$opt" in
        v)
            VERBOSE=true
            ;;
        f)
            OUTPUT_FILE="$OPTARG"
            ;;
        h)
            echo "Usage: $0 [-v] [-f filename]"
            exit 0
            ;;
        \?)
            echo "Invalid option: -$OPTARG" >&2
            exit 1
            ;;
        :)
            echo "Option -$OPTARG requires an argument." >&2
            exit 1
            ;;
    esac
done

# 解析済みオプションを取り除き、残りの位置引数を整列
shift "$((OPTIND - 1))"

main 関数パターンによるスクリプト構造化

スクリプト全体の見通しを良くし、定義順序による不具合を防ぐため、「main 関数」パターンを採用することが推奨されます。

#!/usr/bin/env bash
set -euo pipefail

# ヘルパー関数
log_info() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $*"
}

# メイン処理
main() {
    local input_path="${1:-}"

    if [[ -z "$input_path" ]]; then
        echo "Error: Argument required." >&2
        exit 1
    fi

    log_info "Processing $input_path..."
    # 業務処理
    log_info "Finished successfully."
}

# スクリプトの末尾でエントリーポイントを実行
main "$@"