実践編:関数とシェルスクリプト設計
シバンの選択、関数の定義とローカルスコープの保護、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 "$@"