rcmdnk's blog

前回プロンプトの事を書いたので、 ついでにPROMPT_COMMANDの設定についてメモしておきます。 GNU screenを起動させている時についても。

PROMPT_COMMAND

bashにはPROMPT_COMMANDと言うシェル変数があり、この値に指定したコマンドが 各コマンド実行後に処理されます。

PROMPT_COMMAND='echo command finish'

としておけば、毎回コマンド実行後にcommand finishが表示されます。 (Enterだけ押した場合にも。)

PS1, PS2, PS3, PS4

PS1

さらに前回も使ったPS1と言うシェル変数があり、この内容に書かれた物が プロンプトとして

[host:dir user]$

みたいな感じで表示されます。 他にもPS2PS3PS4と言うシェル変数があります。

PS2

セカンダリプロンプトとして使用され、入力を要求される様なコマンドや、複数行に渡るコマンドなどを使うときに表れます。初期値は>になってます。

[host:dir user]$ echo \
>   <-- this word

PS3

select文を使った時に表示される文字です。初期値は空ですが#? と言う値が表示されます。

select_example.sh
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
select command in "Hello World" "calculate 1+1" exit;do
  if [ "$command" = "exit" ];then
    exit
  elif [ "$command" = "Hello World" ];then
    echo $command
  elif [ "$command" = "calculate 1+1" ];then
    echo $((1+1))
  fi
done

こんな感じのスクリプトを実行した時に出てくる

(-_-) $ ./select_example.sh
1) Hello World
2) calculate 1+1
3) exit
#?  <- this word

この#? 部分です。

PS4

シェルスクリプトで-xオプションを使ったデバッグ時に出てくる文字です。 初期値は+

1
2
3
4
5
6
#!/bin/bash -x
echo ok

ech wrong

echo ok

このような-x付きのデバッグ用スクリプトを実行した時の

+ echo ok
ok
+ ech wrong
./debug_test.sh: line 4: ech: command not found
+ echo ok
ok

この各スクリプト行の前に出る+ の部分です。

通常はPS1以外を変更することは無いと思います。

PS1とPROMPT_COMMANDの使い分け

PS1は内容がプロンプトに表示されるだけなのに対して、 PROMPT_COMMANDでは実際に色々と実行コマンドを行うためのシェル変数です。 PROMPT_COMMANDの方がPS1よりも先に実行されます。

PS1内でも”`“で囲ったり$()を使ったりすることでコマンドを実行できるので、 表示文字が無い様な実行コマンドであれば PS1の中に入れることもできると思いますが、 PROMPT_COMMANDに書いておいた方がすっきりするかと思います。

逆に、PROMPT_COMMANDでも文字を表示させることができるのですが、 下に書くエスケープシーケンスの問題があったり、その他にも 一度書きだした後にバックスペースで消していくと消えてしまったりもするので なるべくPROMPT_COMMANDによる表示はやめておくべきだと思います。

さらに前回の顔文字の例だと、顔文字自体は適当なシェル変数にしてPROMPT_COMMAND で決めて PS1に送る事は出来ると思うのですが、 色指定のエスケープシーケンスを渡す事が上手くできなかったので 直接全部PS1内に書いています。

プロンプトの表示

前回もやったことですが

PS1="[\h \W]\$"

とすればプロンプトが[host dirctory] $の様になりますし、色を指定して

PS1="\[\e[31m\][\h \W]\[\e]m\]\$"

とすれば表示部分が赤くなります。 色を変えるには\e[mで囲った部分に数字を書きます 1。 色などについては どの数字がどのように表示されるかは escseqcheck このスクリプトを実行してみて下さい。 こんな感じの表示がされると思います(MacのiTerm2での結果です) 2

さらに、この様な表示されない文字を表示上無視するために\[\] で文字色指定の部分を囲ってやる必要があります。 これをしなくても一見問題ないように見えますが、 コマンドを消去したり、過去のコマンドをC-pで遡ったりするときに 表示がおかしくなります。

PROMPT_COMMAND内でprintf等で表示しょうとした場合、 \[自体が表示されてしまい、色指定部分をちゃんと隠すことが出来ないので、 表示させるものについてはPS1内に指定するべきです。

さらに、PS1の中では\hはホスト名など等、 特殊なエスケープシーケンス 3 が使えるのでPS1内で行ったほうが簡単です。

ターミナルのタイトルバーの表示

ターミナルのタイトルバーは\e]2;\aで囲った部分に書いた文字を書き出すことで 変更してあげることが出来ます。

printf "\e]2;Hello World!\a"

とすればターミナル上にタイトルバーがる場合はそこへの表示がHello World!に 変わると思います。ただ、これはXterm仕様で、使っているターミナルによっては 違うこともあるようなので気をつけて下さい。 さらに、\e]1;\e]2;の代わりに使うとアイコン名を変えられます。 アイコン名はタスクバーやDockにしまった時にそのWindow名として表れる名前です。 これらを一緒にしたければ\e]0;を使うことで同時に変えられます。

これを直接PROMPT_COMMAND

1
PROMPT_COMMAND='printf "\e]0;%s@%s:%s\a" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'

の様に書いても良いし、PS1に加えて

1
PS1="\[\e]0;\u@\h\w\a\][\h \W]\$ "

の様に書いても同様の結果になります。

エスケープシーケンス 説明
\e]0; ~ \a タイトル名、アイコン名の変更
\e]1; ~ \a アイコン名の変更
\e]2; ~ \a タイトル名変更

GNU screen内での表示

screenを立ち上げると、この辺りの話が少しややこしくなります。 screen内のウィンドウから\e]0;を送ると、各ウィンドウのhardstatusに送られます。 screenの設定でcaptionhardstatusの表示設定で%hで表示される文字列です。

さらにscreen内の各ウィンドウにはタイトルもあり、これはcaption設定などでは %tで設定されるものですが、これを設定するには \ek\e\134で囲った物をprintfします。 ここで、\134\と同義で、PROMPT_COMMAND内で \e\\と使う分には問題ないんですがPS1内で使おうとすると \[\]との組み合わせのなかで何故か]が表示される様に残ってしまうので ここだけ8進法表示にしてあります。 (PS1内では何かしら\\の後にエスケープシーケンスを使わなくてはいけないことに どうしてもなってしまうので、どうも\\\となってしまう部分が微妙におかしくなって しまいます。)

さらに、ターミナルそのもののタイトルをscreen内から変更したいときは \eP\e\134で囲った部分に上で書いたタイトルバー等のエスケープシーケンスを含んだ 情報を入れた送ってあげます。つまり

printf "\eP\e]0;%s@%s:%s\a\e\134" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"

の様にしてあげるとターミナルそのもののタイトルバーとアイコン名を変更して あげることが出来ます。

エスケープシーケンス(screen内) 説明
\e]0; ~ \a ウィンドウのハードステータス
\ek ~ \e\134 ウィンドウのタイトル
\eP ~ \e\134 screen外へ情報を送る

現在のscreen起動時のprompt設定はこんな感じです。 ターミナルの表示は変えずに、screenに入った場所のままになります。 (新規ウィンドウを立ち上げる場所にあたるので たまに立ち上げた場所自体が気になるので)

screen_prompt.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if [[ "$TERM" =~ "screen" ]]; then
  # "\\" doesn't work well, use \134 instead

  # \ek ~ \e\134 : Screen's window title (`\t` in caption/hardstatus)
  # \e]0; ~ \a   : Screen's hardstatus, instead of terminal's title bar (`\h` in caption/hardstatus)
  # If you want to change terminal's title bar, use
  # \eP\e]0; ~ \a\e\134 (\eP ~ \e\134 will send the words out)

  PS1="\[\ek\W\e\134\e]0;\w\a\]\$(\
    ret=\$?
    rand=\$((RANDOM%36));\
    if [ \$ret -eq 0 ];then\
      if [ \$rand -lt 3 ];then
        printf '\[\e[m\](^_^)\[\e[m\] \$ ';\
      elif [ \$rand -lt 5 ];then\
        printf '\[\e[m\](^_-)\[\e[m\] \$ ';\
      elif [ \$rand -lt 6 ];then\
        printf '\[\e[m\](.^.)\[\e[m\] \$ ';\
      else\
        printf '\[\e[m\](-_-)\[\e[m\] \$ ';\
      fi;\
    else\
      if [ \$rand -lt 6 ];then\
        printf '\[\e[31m\](@o@)\[\e[m\] \$ ';\
      elif [ \$rand -lt 12 ];then\
        printf '\[\e[31;1m\](>_<)\[\e[m\] \$ ';\
      elif [ \$rand -lt 18 ];then\
        printf '\[\e[35m\](*_*)\[\e[m\] \$ ';\
      elif [ \$rand -lt 24 ];then\
        printf '\[\e[34m\](T_T)\[\e[m\] \$ ';\
      elif [ \$rand -eq 30 ];then\
        printf '\[\e[34;1m\](/_T)\[\e[m\] \$ ';\
      else\
        printf '\[\e[31m\](>_<)\[\e[m\] \$ ';\
      fi\
    fi;\
    )"
fi

Gistの方はアップデートしないかもしれませんので、一応普段使っている.bashrc の場所も:.bashrc

screenの方では次の様な設定にしてあります。

caption always '%?%F%{=d Wk}%:%{=d Kk}%?%?%P%{=d Bk}%:%?%2n %h%-0L>%=%?%P *** copy/paste mode ***%:%?%-0='
hardstatus alwayslastline "%{= Kk}%-w%40L>%{= Wk}%n %t%{-}%+w"

上記の設定の意味や、他の設定も含めたファイルはこちら .screenrc.

おまけ

$HOMEに置いておくと、さよならしてくれます。

.bash_logout
1
2
# .bash_logout
echo "(-_-)/~ bye!"
Sponsored Links
  1. ここでは\eと使ってますがこれは\033と書いてあげることもできます。 エスケープシーケンスの記号と数字の関係はwikiの ASCIIの項目で。

  2. 30-38が文字色40-48が背景色の設定です。 複数の指定も;で繋げる事により可能で、 1-10と同時に組み合わせると環境によって太字になったり明るくなったりします (上の絵では明るくなっています。) 1-9は

    数字 変化
    1 太字、または明るい色
    2 細字、暗い色
    3 イタリック
    4 一本下線
    5 遅い点滅
    6 速い点滅
    7 反転
    8 隠し文字
    9 表示されるが消去されたものとしてターミナル上扱われる

    となってますが、上の写真にあるように、環境によってはイタリックとかが表示される 代わりに反転になったりします。 点滅はiTermだとできませんでしたが、Macのターミナルなら遅い点滅は表示されました。

    10-20ではフォントを変えるので、場合によっては文字化けするかもしれません。 (上のスクリプトではそれらのあとでは表示後に全て10(Default)に戻しています)

  3. man bashで全て確認出来ますが、良く使うのは次のようなものです。

    エスケープシーケンス 説明
    \h ホスト名(最初の.まで)
    \H ホスト名
    \t 現在時刻(24h, HH:MM:SS)
    \u ユーザーネーム
    \w ディレクトリ名(パス含む)、$HOMEは~に変換
    \W 現在のディレクトリ名のみ、$HOMEは~に変換

Sponsored Links

« プロンプトの戯れ Macでの文字化け対策用のGNU screenのインストール »

}