Bash/プロンプトのカスタマイズ

提供: ArchWiki
2023年10月13日 (金) 23:45時点におけるAshMyzk (トーク | 投稿記録)による版 (→‎カラー: 訳を修正)
ナビゲーションに移動 検索に移動

関連記事

Bash には複数のプロンプトが存在しカスタマイズすることで効率を高めたりセンスを良くしたりオタクっぽくできます。

プロンプト

Bash にはカスタマイズ可能な5つのプロンプト文字列が存在します:

  • PS0 は、コマンドの入力が完了した後で、そのコマンドの出力が開始される前に表示されます。(つまり、コマンドの実行開始直前。)
  • PS1 は、コマンドの入力が開始される前に表示されるプライマリプロンプトです。なので、ほとんどの人がカスタマイズするのはこのプロンプト文字列です。
  • PS2 は、コマンドにさらに多くの入力 (例えば複数行のコマンド) が必要とされる際に表示されるセカンダリプロンプトです。
  • PS3 は滅多に使われません。これは、Bash の select 組み込みコマンドによってインタラクティブメニューが表示されるときに使われるプロンプト文字列です。他のプロンプトと違って、このプロンプトでは Bash のエスケープシーケンスを展開することはできません。通常、このプロンプトは .bashrc ではなく、select が使用されているスクリプト内でカスタマイズします。
  • PS4 も滅多に使われません。Bash スクリプトをデバッグする際に間接参照のレベルを表示するために使われます。最初の文字が繰り返されて、レベルの深さを表します。

全てのプロンプトは、それぞれに対応する変数を設定することでカスタマイズできます (大抵は ~/.bashrc で設定します)。例えば:

PS2='> '

テクニック

プロンプトを普通の文字列に設定することもできますが、様々なテクニックを駆使することでプロンプトをもっとダイナミックに使いやすくすることができます。

Bash のエスケープシーケンス

プロンプトの文字列を表示するとき、Bash は特定のバックスラッシュでエスケープされた文字列を特殊な文字列に変換します。例えば \u は現在のユーザー名になり \A は現在時刻になります。PS1 を '\A \u $ ' と設定すると 17:35 username $ といった具合に表示されます。

エスケープシーケンスの完全なリストは Bash の man ページの中の "PROMPTING" セクションを確認してください。

Terminfo のエスケープシーケンス

Bash によって認識されるエスケープシーケンスの他に、大抵のターミナルは特殊なエスケープシーケンスを認識して表示される文字列だけでなくターミナル自体に変化を起こします。例えば、表示される文字列の色を変更したり、カーソルを任意の場所に移動したり、画面を消去したりすることができます。そのようなエスケープシーケンスは人間が読むことができない形で表記され、ターミナルによって違うこともあります。terminfo データベースでドキュメント化されています。ターミナルがサポートしている機能を確認するには、次のコマンドを実行:

$ infocmp

機能の名前 (= よりも前の部分) は terminfo(5) の man ページを読めば何なのか説明を見ることができます。例えば setaf は出力されるテキストの前景色を設定します。特定の機能のエスケープシーケンスを確認するには、tput コマンドを使います。例:

$ tput setaf 2

上記のコマンドは前景色を緑に設定するエスケープシーケンスを表示します。

ノート: tput コマンドが使えない場合、TERM 環境変数が正しく設定されているか確認してください。例えば xterm-256color ではなく xterm と設定していると tput setaf は0から7までの色番号しか扱えません。

使用できる機能をプロンプトに組み込むときは、Bash のコマンド置換と補完機能を使用できます。例:

~/.bashrc
GREEN="\[$(tput setaf 2)\]"
RESET="\[$(tput sgr0)\]"

export PS1="${GREEN}my prompt${RESET}> "
my prompt>
ノート: Bash の man ページでは tput の出力を \[ \] で囲うことを推奨しています。Bash が出力不可能な文字列を無視するようになるため正確にプロンプトのサイズを計算できるようになります。

ANSI のエスケープシーケンス

残念がら、ANSI のエスケープシーケンスはターミナルの terminfo データベースには載っていないことがあります。特に256色のサポートなど新しい機能のエスケープシーケンスはデータベースに入っていないことがよくあります。tput を使えない場合、エスケープシーケンスを手動で入力する必要があります。

エスケープシーケンスの例は Wikipedia:ANSI escape code を見てください。全てのエスケープシーケンスはリテラルのエスケープ文字から始まり Bash のエスケープシーケンス \e を使って入力できます。例えば \e[48;5;209m は背景を桃色 (ターミナルが256色をサポートしている場合) に設定し、\e[2;2H はカーソルを画面左上に移動します。

Bash のエスケープシーケンスがサポートされていない場合 (PS3 など)、Bash の printf を使うことでリテラルのエスケープ文字を使用できます:

ESC=$(printf "\e")
PEACH="$ESC[48;5;209m"

コマンドの埋め込み

何かコマンドの出力をプロンプトに追加したい場合、コマンド置換を使えばいいと思うかもしれません。例えば、空きメモリ容量をプロンプトに追加しようと以下のように設定した場合:

export PS1="$(awk '/MemFree/{print $2}' /proc/meminfo) prompt > "
53718 prompt >
53718 prompt >
53718 prompt >

上記の設定は上手くいきません。表示されるメモリの容量はいつも同じになってしまいます。原因はコマンドの実行が一回しか行われず、PS1 が一度設定されると、二度と変わらないためです。$ をエスケープするかシングルクォートの中で定義するようにすることで、プロンプトが実際に表示されるときにコマンド置換がされるようになります:

export PS1="\$(awk '/MemFree/{print \$2}' /proc/meminfo) prompt > "
# or
export PS1='$(awk "/MemFree/{print \$2}" /proc/meminfo) prompt > '

コマンドが長い場合は、関数を定義することで PS1 の肥大化を抑えられます:

free_mem()
{
    awk '/MemFree/{print $2}' /proc/meminfo
}

export PS1='$(free_mem) prompt > '
ノート: 置換される関数の中で terminfo/ANSI のエスケープシーケンスを使うことはできますが Bash のエスケープを使うことはできません。特に \[ \] で出力不可能な文字列を囲んでも機能しません。代わりに8進数のエスケープ \001\002 を使ってください (例えば printfecho -e を使用する場合)。

PROMPT_COMMAND

PROMPT_COMMAND 変数を設定することで、PS1 が表示される直前に評価することができます。非常に強力な使い方ができます。例えば特定の条件で PS1 を再定義したり、コマンドを実行したときに Bash の履歴に何らかの操作を加えたりできます。

警告: プロンプトに直接文字列を表示するのに PROMPT_COMMAND は使わないでください。PS1 の外で出力された文字は Bash によってカウントされないため、カーソルの位置がおかしくなったり文字列が消えてしまう現象が発生します。PROMPT_COMMAND を使って PS1 を設定するかコマンドの埋め込みを見てください。

コマンドの入力完了と実行開始の間でエスケープシーケンス

PS1 の末尾でテキストのプロパティをリセットせずにそのままにしておくことで、Bash に入力したテキストにもそのプロパティを反映させることができます。例えば、PS1 の最後に tput blink を追加すると、入力されたコマンドが点滅します。ただし、エンターを押してもテキストのプロパティはリセットされないため、実行されたコマンドの出力にも影響が及びます。

PS0 にエスケープシーケンスを設定することで、コマンドを入力した後、かつそのコマンドが実行される前にそのエスケープシーケンスを挿入することができます。あるいは、コマンドが実行される直前に送信される Bash の DEBUG シグナルを捕捉することで、同じことをすることができます:

$ trap 'tput sgr0' DEBUG

root プロンプトのカスタマイズ

root で実行していることを知らせるために、root プロンプトはわかりやすいようにカスタマイズすると良いでしょう (赤字の点滅など)。root のホームディレクトリ (/root) を使うことで通常の Bash プロンプトのカスタマイズと同じようにカスタマイズできます。最初にスケルトンファイルの /etc/skel/.bash_profile/etc/skel/.bashrc/root にコピーしてから /root/.bashrc を編集してください。

サンプル

ヒント: infocmp は、tput で利用できる色の数を表示します。例: colors#8 (8つの色が利用可能)。

ターミナルでサポートされている色の完全な範囲は、tput を単純なループで回すことで確認できます (背景色ではなく前景色で表示するには setabsetaf に置き換えてください)。

for C in {0..255}; do
    tput setab $C
    echo -n "$C "
done
tput sgr0
echo

上記のコマンドが動かない場合 (そして、TERM に適切な値を設定しても問題が解決できない場合)、別のエスケープシーケンスでテストすることもできます:

# 標準色
for C in {40..47}; do
    echo -en "\e[${C}m$C "
done
# 高輝度色
for C in {100..107}; do
    echo -en "\e[${C}m$C "
done
# 256色
for C in {16..255}; do
    echo -en "\e[48;5;${C}m$C "
done
echo -e "\e(B\e[m"

エスケープシーケンスを背景色から前景色に変更したい場合は、標準色の範囲を 30..37 に、高輝度色の範囲を 90..97 にし、256色ではエスケープシーケンスの 48 の部分を 38 に変更してください。

一般的な機能

以下の terminfo の機能はプロンプトをカスタマイズするときに使うことができ多くのターミナルがサポートしています。#1#2 は実際に使うときに数字に置き換えてください。

機能 エスケープシーケンス 説明
テキストの属性
blink \E[5m テキストの点滅をオン
bold \E[1m 太字テキストをオン
dim \E[2m 淡色テキストをオン
rev \E[7m 逆転表示をオン (テキスト色と背景色が切り替わります)
sitm \E[3m 斜字テキストをオン
ritm \E[23m 斜字テキストをオフ
smso \E[7m テキストのハイライトをオン
rmso \E[27m テキストのハイライトをオフ
smul \E[4m テキストの下線をオン
rmul \E[24m テキストの下線をオフ
setab #1 \E[4#1m 背景色を #1 (0-7) に設定
setaf #1 \E[3#1m テキスト色を #1 (0-7) に設定
sgr0 \E(B\E[m テキストの属性をリセット
カーソルの移動
sc \E7 カーソルの位置を保存
rc \E8 保存したカーソルの位置に戻す
clear \E[H\E[2J 画面書を消去してカーソルを一番左に移動
cuu #1 \E[#1A #1 行分だけカーソルを上に移動
cud #1 \E[#1B #1 行分だけカーソルを下に移動
cuf #1 \E[#1C #1 列分だけカーソルを右に移動
cub #1 \E[#1D #1 列分だけカーソルを左に移動
home \E[H カーソルを左端に移動
hpa #1 \E[#1G カーソルを #1 列目に移動
vpa #1 \E[#1d カーソルを #1 行目の1列目に移動
cup #1 #2 \E[#1;#2H カーソルを #1 行目の #2 列目に移動
文字列の削除
dch #1 \E#1P (バックスペースと同じように) #1 文字を削除
dl #1 \E#1M #1 行を削除
ech #1 \E#1X #1 文字を消去 (カーソルの移動は伴わない)
ed \E[J 画面下端まで消去
el \E[K 行末まで消去
el1 \E[1K 行頭まで消去

終了コードの視覚化

コマンドの埋め込みと同じ方法で $? などの特殊な Bash 変数の補完を遅延させることができます。以下のプロンプトは前に実行したコマンドの終了コードを表示します:

export PS1="\$? > "
# or
export PS1='$? > '
0 > true
0 > false
1 >

条件と関数を使うことでさらにわかりやすくすることができます:

exitstatus()
{
    if [[ $? == 0 ]]; then
        echo ':)'
    else
        echo 'D:'
    fi
}
export PS1='$(exitstatus) > '
:) > true
:) > false
D: >

カーソルの位置

PS1 の中でカーソルを移動することで様々な場所にプロンプトを表示させることができます。ただし、何か別のものを出力した後は元の位置にカーソルを移動しなくてはなりません。tput の scrc 機能を使うことでカーソルの位置を保存して後で戻すことができます。カーソルを移動するプロンプトを作るときは一般的に以下のようにします:

export PS1="\[$(tput sc; cursor-moving code) positioned prompt stuff $(tput rc)\] normal prompt stuff"

再配置するプロンプトのブロックは \[ \] で囲って Bash が通常のプロンプトとしてカウントしないようにしてください。

テキストを右に整列

printf を使うことで簡単に画面右側にテキストを表示できます:

rightprompt()
{
    printf "%*s" $COLUMNS "right prompt"
}

export PS1='\[$(tput sc; rightprompt; tput rc)\]left prompt > '
right promptleft prompt >

上記の設定では右側に表示する変数サイズのフィールド %*s を作成してサイズをターミナルの列数の正確な数 $COLUMNS に設定しています。

任意の位置

cup 機能では画面の特定の位置にカーソルを移動できます。例えば tput cup 20 5 は20行目5列目にカーソルを移動します (0行目0列目が左上です)。cuu, cud, cuf, cub (上, 下, 右, 左) は現在の位置から相対的にカーソルを移動します。例えば tput cuf 10 はカーソルを10文字分だけ右に移動します。引数で LINESCOLUMNS 変数を使うことで下端と右端からの相対位置にカーソルを移動できます。例えば、右下から10行目5列目にカーソルを移動するには:

$ tput cup $((LINES - 11)) $((COLUMNS - 6))

ターミナルのウィンドウタイトルのカスタマイズ

プロンプトと同じようにシェルにエスケープシーケンスを出力することでターミナルのウィンドウタイトルもカスタマイズできます。プロンプトでウィンドウタイトルのカスタマイズすることができます。技術的には xterm の機能ですが、近代的なターミナルの多くがカスタマイズをサポートしています。使用するエスケープシーケンスは ESC]2;new titleBEL です。ESCBEL はエスケープとベルの文字列に置き換えてください。Bash のエスケープシーケンスを使用する場合、以下のように設定することでプロンプトのタイトルを変えられます:

export PS1='\[\e]2;new title\a\]prompt > '

もちろんウィンドウタイトルの文字列にはコマンドの出力結果を含めたり $PWD などの変数を使えます。

参照