LoginSignup
8
7

More than 5 years have passed since last update.

Bash で PROMPT_COMMAND を使わずに PS1 を動的に変更

Posted at

はじめに

PROMPT_COMMAND とは

PROMPT_COMMAND に関数やコマンドがセットされていると、Bash でプロンプトを表示する直前に、毎回それが実行されます。

この機能を利用して PS1 を動的に書き換える例を見かけることがあります。

PS1 内でコマンドの出力を表示する

しかし、PS1 内でも関数やコマンドを実行して、結果の文字列を表示することができます。1

ただ、うろ覚えでこの辺りの設定をしていたところ、少々ハマってしまったので、実際に自分が間違えた例と合わせて、正しい設定方法を記しておきます。

よく使われる git-completion を題材に用います。
※以降の例で、git-prompt.sh 相当のものは読み込み済みとします。

間違い例1

# ~/.bashrc
__my_ps1() {
  local branch=$(__git_ps1)
  [[ $branch ]] && branch="\[\e[0;35m\]$branch\[\e[0m\] "
  echo "[${branch}\u@\h:\W] "
}

PS1="$(__my_ps1)"

一見ちゃんと動きそうで、実際動きはするのですが、PS1 の文字列は最初にこのスクリプトを評価したときのままで、ディレクトリを移動しても、__my_ps1 関数が再評価されることはありません。

実行例:

[me@localhost:~] cd path/to/gitrepo
[me@localhost:gitrepo] # branch は表示されない
[me@localhost:gitrepo] . ~/.bashrc # 再読み込み
[ (master) me@localhost:gitrepo] # 表示される
[ (master) me@localhost:gitrepo] cd
[ (master) me@localhost:~] # 非gitディレクトリでも表示されてしまう

(例では、カラーコードは反映されていません。)

間違い例2

# ~/.bashrc
__my_ps1() {
  local branch=$(__git_ps1)
  [[ $branch ]] && branch="\[\e[0;35m\]$branch\[\e[0m\] "
  echo "[${branch}\u@\h:\W] "
}

PS1='$(__my_ps1)'

この場合、__my_ps1 関数は毎回実行・評価されてくれるのですが、エスケープシーケンスが評価されず、文字列としてそのまま表示されてしまいます。

実行例:

[\u@\h:\W] cd path/to/gitrepo
[\[\e[0;35m\] (master)\[\e[0m\] \u@\h:\W] # branch は表示される
[\[\e[0;35m\] (master)\[\e[0m\] \u@\h:\W] cd
[\u@\h:\W] # branch は表示されない

なんとも残念な見た目ですorz

正しい例

正しくは、下のように PS1 内で $(関数 or コマンド) をシングルクォートで記述します。

# ~/.bashrc
PS1='[\[\e[0;35m\]$(__git_ps1)\[\e[0m\] \u@\h:\W] '

ダブルクォートで括りたい場合は、 "\$(__git_ps1)" のようにエスケープすれば OK です。

実行例:

[me@localhost:~] cd path/to/gitrepo
[ (master) me@localhost:gitrepo] # branch は表示されない

以上です。

参考になれば幸いです。

参考


  1. 余談ですが、筆者は PROMPT_COMMAND で PS1 を書き換えて先輩に注意を受けたことがあります。 

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7