私のシェルプロンプト遍歴

入社一年目の @sakuro です。

プログラマーが日々使うツールの中には、カスタマイズに無限の時間を消費できてしまうものがあります。

  • ウィンドウマネージャ
  • エディタ
  • そしてシェル

日々使うものであればあるほど、使って心地よい状態でないとストレスになるので、納得できるカスタマイズを行い、また繰り返し改善を進めていくことになるでしょう。

そんな私のツールカスタマイズの中から、今回はシェルプロンプトに関する設定を振り返ってみたいと思います。

大昔

初めて使った UNIX 系 OS は SunSO4.1.3 でした。当時は tcsh を使っていました。 LinuxmacOS (Mac OS X) を使うようになって bash をメインに、その後 zsh を使うようになります。

この頃のプロンプトには

%

$

といった簡素なものを使っていました。

右プロンプトを知る

zsh のマニュアルを読んで、 右プロンプト なるものが存在することを知った私は、こんな定義を使うようになります。

PROMPT='%n@%m%# '
RPROMPT='%~'

左の通常プロンプトとして ユーザー名@ホスト名%␣ 、右プロンプトとして カレントディレクトリ を表示しています。

再現画像(右プロンプト)

右プロンプトはタイプしているコマンド行が長くなってくると勝手に消えるので邪魔にはなりません。

prompt 関数とテーマを知る

zshを使い始めてしばらく経ち、zsh に付属する関数の中に prompt というものがあることを知りました。

# 初期化
autoload -U promptinit
promptinit
# インストールされているテーマの名前を一覧
prompt -l
# テーマをプレビュー
prompt -p テーマ名
# テーマを切り替える
prompt テーマ名

このようにあらかじめ定義されているテーマの中から名前を指定して切り替えることが出来ます。

テーマの実体は prompt_テーマ名_setup というシェル関数として定義されており、 prompt テーマ名 を実行すると、この関数が実行されます。中では

  • プロンプト表示に使われる変数 (PROMPTRPROMPT) を直接設定
  • フック関数 (コマンド実行前フック: preexec やプロンプト表示前フック: precmd) を登録

などを駆使して、独自のプロンプトを作っています。

プレビューで標準付属のテーマを眺めてみたのですが、残念ながらどれも趣味に合いません。

とりあえずは

prompt_sakuro_setup() {
  PROMPT='%n@%m%# '
  RPROMPT='%~'
}

として自分のプロンプトも prompt フレームワークの上に乗ることにします。

このあたりから本格的にプロンプトのカスタマイズを始めていくことになりました。

vcs_info を知る

さらにしばらく経った後、今度は zsh に付属するライブラリに vcs_info という関数があることを知ります。

カレントディレクトリに対する VCS (Version Control System) に関する情報を取得するための関数で、これを使うことでプロンプトにカレントディレクトリ(以下)の CVSSubversion (時代!) や Mercurial (hg) や Git の情報を表示することが出来るようになりました。

この頃は紹介記事を鵜呑みにして設定しています。

zstyle ':vcs_info:*' formats '%F{blue}%b%f' '%S'
zstyle ':vcs_info:*' actionformats '%b%F{cyan}:%F{red}%a%f'
zstyle ':vcs_info:svn:*' branchformat '%F{blue}%b%F{cyan}:%fr%r'
zstyle ':vcs_info:*' enable git svn hg bzr cvs

zstyle はコマンド行補完機能の細かい挙動の制御に使われているシステムですが、パターンをキーにした簡単な設定データベースとしても活用されています。 vcs_info では、ブランチ名や実行中の処理(rebase -i など)の表示スタイルの設定などに利用しています。

凝りだす

細かく調整を繰り返しながら数年が経ちました。

その間には、たとえば、直前のコマンドの成否によってプロンプトに 😀 もしくは 😡 を表示するこんな設定をしていたことがあります。

PROMPT="...%(?,😀,😡) "

こういうシンプルなものならよいのですが、凝ったことも結構やっていました。

再現画像(リポジトリルートで色分け)

たとえばこれは、リポジトリまでのパスとリポジトリ内のパスを色分けしています。なおかつ、リポジトリまでのパスの要素は最後の1つ(=リポジトリ名)を除いて、先頭1文字だけに短縮しています。

いろいろやっているうちに保守が面倒になり、自作を完全に放棄して人任せにしたくなってきました。

プロンプト生成システムを使ってみる

pure

最初に見つけたのは、 pure です。

情報行とプロンプト行の2行を使うプロンプトで、コンパクトな中に高密度の情報を出力してくれます。

特徴的なのは、 git のリモートリポジトリの情報を取得するために、zshで書かれた非同期処理ライブラリを使っているところでしょうか。プロンプトを表示後、リモートの情報を取得してきて、push/pullすべき差分の有無を所定の位置に表示してくれます。

また、コマンド終了までに時間がかかった場合はその時間を表示してくれる機能もあります。

starship

最近人気なのが starship です。Rustで書かれています。 その前に spaceship という Go で書かれた先達が存在するのですが、ユーザーが集まったのは starship のほうでした。プロモーションへの力の入れ方の違いでしょうか。

さまざまな言語のバージョンや、リポジトリの状態、その他(AWSのアカウントやバッテリーの残量 etc.) の表示機能をモジュールに分け、拡張しやすい作りになっています。

version 0.44 までは、各モジュールにおいて表示フォーマットがハードコーディングされており、やたらと in とか on のような前置詞をつけたいたずらに長い表示になっていました。書き換えるにはソースコードを変更するしかなかったのです。これが 0.45 で 統一的にフォーマットを設定 できるようになって使いやすくなりました。

0.45 が出る前は、出たら早速乗り換えよう、と待ちかまえていましたが、いざリリースされて実際に使ってみたところ、設定を弄っても望む出力にならない箇所が何点かあることに気付いてしまいます。

自作に戻る

starship の設定をやってみて、やはり自分が望む出力は自分で作るのがベストという考えに戻ってきました。

現在は以下のようなプロンプトを使っています。

現在のプロンプト

  • 1m10s は pure から借用した、前回のコマンド実行の経過時間
  • ユーザー名@ホスト名 のホスト名の部分はロードアベレージを取得して色を変えています。 Mac では sysctl コマンドを実行していますが、 procファイルシステムがある環境では $(< /proc/loadavg) を使って外部コマンドを起動せずに取得しています。
  • ディレクトリのパス部分は以前使っていたもの
  • そのあとにブランチ名(each-subcommand)・Rubyのバージョン・AWSのプロファイル名を表示。
  • 前置されている記号は Cica フォントを使っています。
  • プロンプト本体である $ は、直前のコマンドの終了ステータスによって緑か赤で表示されます。

今後は…

追加したい情報はいくつかありますが、中でも git の ahead / behind の情報 を非同期で取得して表示する pure プロンプトの機能は取り込んでみたいものです。

zshはやたらと記述能力が高く、外部コマンドを使わなくてもかなりのことが出来ます。(あとで読んで訳が分からなくなることも多々) 今後もマニュアル首っ引きで、プロンプトの改善に勤しんでいこうと思います。