直前コマンドの終了ステータスをシグナル名と一緒に表示するBashプロンプト

先日、自分も周りのエンジニアに倣って自分の dotfiles を公開した。
https://github.com/ryotosaito/dotfiles
その時にステータスの詳細を表示するプロンプトが好評だったのでブログでも公開する。

TL;DR

Bash の連想配列を使って終了ステータスとシグナル名をマッピングすれば、覚えてなくても一発で何のシグナルかわかるようになる。

上の画像は自分が使ってるプロンプトだが、余計なものを省いたコードが以下になる。
(Bash 4.0以上のみ対応)

PROMPT_COMMAND=__prompt_command
__prompt_command() {
	# 最初に直前コマンドの終了ステータスを記録
	local status=$?
	# SGRパラメータ(文字装飾:色付けを行うためのエスケープシーケンス)
	local reset='\e[m' red='\e[31m' green='\e[32m' blue='\e[34m'
	# 連想配列で終了ステータスとシグナル名の対応を管理
	local -A err_code=(
		[1]=error [2]='builtin error' [126]='not executable'[127]='command not found'
		[128]=SIGHUP [129]=SIGINT [130]=SIGQUIT [131]=SIGILL [132]=SIGTRAP
		[133]=SIGABRT [134]=SIGEMT [135]=SIGFPE [136]=SIGKILL [137]=SIGBUS
		[138]=SIGSEGV [139]=SIGSYS [140]=SIGPIPE [141]=SIGALRM [142]=SIGTERM
		[143]=SIGURG [144]=SIGSTOP [145]=SIGTSTP [146]=SIGCONT [147]=SIGCHLD
		[148]=SIGTTIN [149]=SIGTTOU [150]=SIGIO [151]=SIGXCPU [152]=SIGXFSZ
		[153]=SIGVTALRM [154]=SIGPROF [155]=SIGWINCH [156]=SIGINFO [157]=SIGUSR1
		[158]=SIGUSR2
	)
	# ユーザ名@ホスト名:ディレクトリ
	PS1="\[$green\]\u@\h\[$reset\]:\[$blue\]\w\[$reset\] " 
	# ステータス0の時は?、それ以外は?(SIGNAL)
	if [[ $status -eq 0 ]]
	then
		PS1+="?"
	else
		PS1+="? \[$red\]$status(${err_code[$status]})\[$reset\]" 
	fi
	# プロンプト(\nを消すと改行しなくなる)
	PS1+='\n\$ '
}

これを .bashrc ないし .bash_profile に書けば良い(筆者はどっちでもいいと思ってる。だって基本 .bash_profile から .bashrc 読み込むしログインシェル以外そんなに使わないし)。

終了ステータス

POSIX シェルおよび Bash での終了ステータスは以下の通りである。

  • 0:正常終了
  • 1~125:異常終了
  • 126:実行不可能なファイルを実行しようとした
  • 127:コマンドや関数が見つからない
  • 127+n:シグナル n によって停止(例えばみんな大好き kill -9 (SIGKILL) で止められると136となる)

またBashに限り、組み込みエラーが発生した場合は終了ステータスが 2 となる。

自分の .bashrc には 3~125 の終了ステータスが考慮されてないが、特に困るものでもないし参照外エラーのようなものは起きないので放置している。
ちなみに終了ステータスは 255 まで認められているが、ユーザ定義の終了ステータスは125までが理想とされている。

POSIX シェルおよび Bash では直前コマンドの終了ステータスを $? で取得することができる。ただしプロンプト関数実行中に他の処理を挟んでしまうと上書きされてしまうため、必ず真っ先にステータスを控える必要がある。

Bashの連想配列

Bash には v4.0 から連想配列が導入された。
これを用いることで、エラーコードをキー、シグナル名を値として参照しやすくすることができる。

local -A variable_name を使うと宣言できる(ローカル変数でない場合は代わりに declare -Atypeset -A を使う)。値を追加する時は array[key]=value 、削除する際は unset array[key] と記述する。
参照する際は必ず中括弧をつけて ${array[key]} とする。この時キーが存在しなかったら空文字列が返される。

PROMPT_COMMAND

Bash では .bashrc.bash_profile で直接 PS1 変数(プロンプト)を記述する他に、 PROMPT_COMMAND 変数に関数名を設定することによって毎回プロンプトを設定するための関数を叩くことができる。
これを利用して終了ステータスから動的にプロンプトを生成する。

まとめ

せっかくカッコよく TL;DR って書いたのに全然 too long じゃなかった

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です