Bash 5.1 の機能と改善点
2020年12月にリリースされたBash 5.1は、最新のシェルスクリプティング向けにいくつかの注目すべき機能と改善を導入しました。
新しいシェル変数
EPOCHSECONDS と EPOCHREALTIME
現在時刻にアクセスするための2つの新しい特殊変数:
# 現在のUnixタイムスタンプ(秒)を取得
echo "現在のタイムスタンプ: ${EPOCHSECONDS}"
# マイクロ秒精度で現在のUnixタイムスタンプを取得
echo "高精度タイムスタンプ: ${EPOCHREALTIME}"
# 実用的な例: シンプルなベンチマーク
start="${EPOCHREALTIME}"
# ... 何らかの処理 ...
sleep 0.5
end="${EPOCHREALTIME}"
duration=$(awk "BEGIN {print ${end} - ${start}}")
echo "処理にかかった時間: ${duration} 秒"
# 例: 高精度タイムスタンプ付きでログ出力
log_message() {
printf '[%s.%06d] %s\n' \
"$(date -d "@${EPOCHSECONDS}" '+%Y-%m-%d %H:%M:%S')" \
"${EPOCHREALTIME#*.}" \
"$*"
}
log_message "プロセスを開始"
使用例:
- 正確なパフォーマンス計測
- ログ出力用の高解像度タイムスタンプ
- 外部の
dateコマンド呼び出しの回避 - スクリプト内でのマイクロ秒精度のタイミング
SRANDOM 変数
暗号学的に強力な乱数:
# 安全な乱数を生成(利用可能な場合)
if [[ -n "${SRANDOM}" ]]; then
secure_random="${SRANDOM}"
echo "安全な乱数: ${secure_random}"
else
echo "SRANDOM は利用できません(getrandom/getentropy が必要です)"
fi
# 実用的な例: 安全な乱数名で一時ファイルを生成
temp_file="/tmp/secure_${SRANDOM}_${EPOCHSECONDS}.tmp"
注意: SRANDOM を使用するには、システムが getrandom() または getentropy() システムコールをサポートしている必要があります。
改善されたリダイレクト構文
{varname} リダイレクト
ファイルディスクリプタを変数に割り当て、ファイルハンドルの管理を改善:
# ファイルディスクリプタを開き変数に格納
exec {fd}< input.txt
while IFS= read -r -u "${fd}" line; do
echo "読み取り: ${line}"
done
exec {fd}<&- # ファイルディスクリプタを閉じる
# 実用的な例: 複数のファイルハンドル
exec {input_fd}< data.txt
exec {output_fd}> results.txt
exec {error_fd}> errors.txt
process_data() {
while IFS= read -r -u "${input_fd}" line; do
if validate_line "${line}"; then
echo "処理済み: ${line}" >&"${output_fd}"
else
echo "エラー: ${line}" >&"${error_fd}"
fi
done
}
process_data
exec {input_fd}<&-
exec {output_fd}>&-
exec {error_fd}>&-
利点:
- マジックナンバーではなく名前付きファイルディスクリプタ
- 自動FD割り当てにより競合を回避
- より読みやすく保守しやすいコード
- オープンファイルハンドルの追跡が容易
配列の強化
連想配列の扱いの改善
複雑な配列操作のサポート強化:
# typeset で連想配列を宣言
declare -A config=(
[host]="localhost"
[port]="8080"
[debug]="true"
)
# 強化された配列展開
for key in "${!config[@]}"; do
printf '%s=%s\n' "${key}" "${config[${key}]}"
done
# 実用的な例: 設定パーサー
parse_config() {
declare -gA app_config
local line key value
while IFS='=' read -r key value; do
[[ "${key}" =~ ^[[:space:]]*# ]] && continue # コメントをスキップ
[[ -z "${key}" ]] && continue # 空行をスキップ
# 空白をトリミング
key="${key#"${key%%[![:space:]]*}"}"
key="${key%"${key##*[![:space:]]}"}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
app_config["${key}"]="${value}"
done < config.ini
}
多次元配列のサポート
入れ子になった配列構造の扱いが改善されました。
Readline 8.1 の統合
テキスト編集とコマンドライン操作の強化:
キーバインドと履歴
# ~/.inputrc での readline 設定
# 大文字小文字を区別しない補完を有効化
set completion-ignore-case on
# 補完候補の可視統計情報を有効化
set visible-stats on
# 曖昧な場合にすべての補完候補をすぐに表示
set show-all-if-ambiguous on
# 補完マッチングに色を使用
set colored-completion-prefix on
改善された履歴検索
# スクリプトまたは .bashrc 内で
# Ctrl+R で逆方向インクリメンタル検索(デフォルトですが、5.1 で強化)
bind '"\C-r": reverse-search-history'
# Ctrl+S で順方向インクリメンタル検索
bind '"\C-s": forward-search-history'
# 改善された履歴処理
shopt -s histappend # 履歴に追記
shopt -s cmdhist # 複数行コマンドを1エントリとして扱う
HISTCONTROL=ignoreboth # 重複と先頭スペースを無視
HISTSIZE=10000
HISTFILESIZE=20000
シグナル処理の改善
サブシェルとプロセス置換におけるシグナル伝播の改善:
# サブシェルでのトラップ処理の強化
cleanup() {
echo "クリーンアップ中..." >&2
rm -f "${temp_file}"
exit 130 # 128 + SIGINT
}
trap cleanup SIGINT SIGTERM
temp_file=$(mktemp)
# シグナルがパイプラインを通じて適切に伝播
long_running_task | process_output &
pid=$!
# 適切なシグナル処理でバックグラウンドジョブを待機
wait "${pid}" 2>/dev/null
exit_code=$?
if [[ ${exit_code} -gt 128 ]]; then
signal=$((exit_code - 128))
echo "プロセスがシグナル ${signal} で終了しました" >&2
fi
バグ修正とエッジケース
サブシェルの処理
サブシェル生成時の動作改善:
# より信頼性の高いサブシェル変数の継承
outer_var="親"
(
# サブシェルは親の変数をより確実に継承
echo "サブシェル内: ${outer_var}"
inner_var="子"
)
# outer_var はまだアクセス可能、inner_var は不可
echo "サブシェル後: ${outer_var}"
パターンマッチングのエッジケース
グロブパターンマッチングのエッジケースを修正:
# 特殊文字を含む、より信頼性の高いグロブマッチング
shopt -s nullglob # マッチしないグロブは空の展開に
shopt -s extglob # 拡張パターンマッチング
# 例: ファイルをマッチングし、マッチしない場合は適切に処理
files=(*.txt)
if [[ ${#files[@]} -eq 0 ]]; then
echo ".txt ファイルが見つかりません"
else
printf '見つかりました: %s\n' "${files[@]}"
fi
パフォーマンスの改善
- ループ内での変数展開の高速化
- 配列操作の最適化
- 大規模配列のメモリ使用量削減
- パターンマッチングの効率改善
互換性に関する注意
Bash 5.0 からのアップグレード
ほとんどのスクリプトは互換性がありますが、以下の点に注意:
EPOCHSECONDSとEPOCHREALTIMEは新しいため、古いバージョンを対象とする場合は存在確認を行うSRANDOMはすべてのシステムで利用できるとは限らない- 展開動作の一部のマイナーなエッジケースが修正された
バージョンチェック
# Bash 5.1 の機能を使用する前にバージョンを確認
if [[ "${BASH_VERSINFO[0]}" -ge 5 ]] && [[ "${BASH_VERSINFO[1]}" -ge 1 ]]; then
# Bash 5.1 の機能を安全に使用
timestamp="${EPOCHSECONDS}"
else
# 古いバージョンのフォールバック
timestamp=$(date +%s)
fi
参考資料
- Bash NEWS ファイル - 公式リリースノート
- GNU Bash 5.1 リリース - ソース配布
- Bash マニュアルページ:
man bash(バージョン固有の機能に関するセクション)
追加リソース
より広範なBash開発パターンとベストプラクティスについては、以下を参照:
- ../bash-development/SKILL.md - コアBash開発パターン
- ../bash-portability/SKILL.md - クロスバージョン互換性