Bashスクリプトの品質向上に役立つ実用的な開発ツールを紹介する。引数の可視化、ターミナル出力の装飾、コマンドラインオプションの解析という3つの課題に対し、実戦で検証されたソリューションを解説する。
引数デバッガ:シェルパラメータの可視化
スクリプト間で引数が正しく渡されているか確認する際、単なるecho "$@"では位置情報や境界処理が分かりにくい。以下のツールは引数を順序付きで色分け表示し、開発効率を大幅に向上させる。
実装コード
#!/usr/bin/env bash
set -eEuo pipefail
calcDigits() {
local n=$1 len=0
while ((n != 0)); do
((++len))
((n /= 10))
done
echo "$len"
}
total=$#
pad=$(calcDigits $total)
readonly C_RED='\e[1;31m' C_CYAN='\e[1;36m' C_OFF='\e[0m'
outputArg() {
local pos=$1 val=$2
if [[ -t 1 ]]; then
printf "%${pad}d/%d: ${C_RED}[${C_CYAN}%s${C_RED}]${C_OFF}\n" "$pos" "$total" "$val"
else
printf "%${pad}d/%d: [%s]\n" "$pos" "$total" "$val"
fi
}
outputArg 0 "$0"
i=1
for v; do
outputArg $((i++)) "$v"
done
動作確認例
$ ./arg-debug.sh "file 1.txt" 'single' $'multi\nline' *.sh
0/4: [./arg-debug.sh]
1/4: [file 1.txt]
2/4: [single]
3/4: [multi
line]
4/4: [deploy.sh]
応用パターン
関数の入出力検証に組み込む例:
verifyParams() {
local func=$1
shift
[[ "${TRACE:-}" == "1" ]] && {
echo "=== $func ==="
/path/to/arg-debug.sh "$@"
}
"$func" "$@"
}
ターミナルカラー制御ライブラリ
ANSIエスケープシーケンスを安全に扱うためのラッパー関数群。パイプ出力時の自動無効化や、複合スタイルの簡潔な指定を実現する。
コア関数
cPrint() {
local style=$1; shift
[[ -t 1 ]] && printf '\e[%sm%s\e[0m\n' "$style" "$*" || printf '%s\n' "$*"
}
cPrintNoLF() {
local style=$1; shift
[[ -t 1 ]] && printf '\e[%sm%s\e[0m' "$style" "$*" || printf '%s' "$*"
}
スタイル指定表
| コード | 効果 | 用途 |
|---|---|---|
| 0;30 | 黒文字 | 補助情報 |
| 1;31 | 赤太字 | エラー表示 |
| 1;32 | 緑太字 | 成功通知 |
| 1;33 | 黄太字 | 警告 |
| 0;34 | 青文字 | リンク・パス |
| 0;35 | マゼンタ | 強調 |
| 0;36 | シアン | 進行状況 |
| 0;37;41 | 白字赤背景 | 重大エラー |
| 0;30;43 | 黒字黄背景 | 注意喚起 |
状態連動出力例
statusOut() {
local code=$1 msg=$2
case $code in
0) cPrint "1;32" "✓ $msg" ;;
1) cPrint "1;31" "✗ $msg" ;;
2) cPrint "1;33" "⚠ $msg" ;;
*) cPrint "0;36" "ℹ $msg" ;;
esac
}
# 使用例
statusOut $? "バックアップ処理"
拡張オプションパーサ
従来のgetoptsやgetoptの制限を克服し、長形式オプション・複数値引数・厳密な検証を統合した解析エンジン。
構文定義
モード文字列でオプションの振る舞いを指定する:
# パターン: 短名,長名[:モード]
# モードなし: 真偽値
# : : 単一値必須
# + : 複数値(配列)
"q,quiet|o,output:|I,include+"
# → -q / --quiet : フラグ
# → -o / --output : 単一値
# → -I / --include : 複数値
利用例
#!/usr/bin/env bash
source "./lib/optParser.sh"
optParser "v,verbose|d,depth:|p,path+" "$@"
[[ "${_OPT_FLAG_verbose}" == "true" ]] && set -x
[[ -n "${_OPT_VAL_depth}" ]] && {
echo "検索深度: ${_OPT_VAL_depth}"
}
((${#_OPT_ARR_path[@]} > 0)) && {
echo "検索パス: ${_OPT_ARR_path[*]}"
}
echo "その他の引数: ${_OPT_REST[*]}"
実行サンプル
$ ./finder.sh -v --depth 3 --path /home --path /var --name "*.log"
検索深度: 3
検索パス: /home /var
その他の引数: --name *.log
エラーハンドリング
| 終了コード | 状況 |
|---|---|
| 221 | 無効なオプション定義(予約文字使用など) |
| 230 | 不正なオプション形式(---optなど) |
| 231 | 必須値の欠落 |
optParser "f,file:" "$@" || {
echo "引数解析エラー (コード: $?)" >&2
exit 1
}
堅牢性を高める実行モード
本ツールセット全体で採用しているset -eEuo pipefailの効果と対処法を解説する。
各オプションの役割
-e(errexit)- コマンドが非ゼロ終了時に即座に終了。失敗の連鎖を防ぐ。
-E(errtrace)- ERRトラップをサブシェル・関数に継承。一貫したエラー捕捉を保証。
-u(nounset)- 未設定変数の参照を即座にエラー。タイポによる予期せぬ動作を防止。
-o pipefail- パイプライン全体で失敗を検出。最終コマンドのみの成功に依存しない。
未設定変数への対処
# デフォルト値の付与
: "${WORK_DIR:=/tmp/work}"
# 必須チェック(未設定時はメッセージ付き終了)
: "${API_KEY:?API_KEY環境変数が必要です}"
# 空文字列と未設定の区別が必要な場合
: "${DEPLOY_TARGET:?}" # 空でも設定されていれば通過
一時的な緩和
特定のコマンドで許容が必要な場合:
# コマンド単位で無効化
grep pattern file || true
# サブシェルで局所的に変更
result=$(set +e; may_fail_command; echo $?)
クリーンアップ保証
cleanup() {
[[ -d "${TEMP_DIR:-}" ]] && rm -rf "$TEMP_DIR"
}
trap cleanup EXIT INT TERM ERR
統合活用パターン
3つのツールを組み合わせた実用的なスクリプト構成:
#!/usr/bin/env bash
set -eEuo pipefail
# ツール読み込み
SCRIPT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
source "${SCRIPT_ROOT}/lib/optParser.sh"
source "${SCRIPT_ROOT}/lib/colorLib.sh"
# オプション解析
optParser "c,config:|v,verbose|h,help" "$@" || {
cPrint "1;31" "引数エラー"; exit 1
}
[[ "${_OPT_FLAG_help}" == "true" ]] && { showHelp; exit 0; }
# 設定検証
: "${_OPT_VAL_config:?設定ファイルを -c で指定してください}"
# デバッグモード
[[ "${_OPT_FLAG_verbose}" == "true" ]] && {
cPrint "0;36" "実行パラメータ:"
"${SCRIPT_ROOT}/tools/arg-debug.sh" "$@"
}
# メイン処理
cPrintNoLF "0;36" "処理中... "
processData "${_OPT_VAL_config}"
cPrint "1;32" "完了"
# 後片付け
trap '' EXIT # 正常終了時はクリーンアップ不要
これらのツールは個別に利用可能であり、既存のスクリプトに段階的に導入できる設計となっている。