LoginSignup
7
5

More than 1 year has passed since last update.

ZSHのPROMPTを短くする(PWD, VIRTUAL_ENV)

Posted at

真面目に仕事しようとしたら、私のプロンプトがエライことになってた。

スクリーンショット 2021-10-11 1.46.47.png

長いわ!流石に長いわ!こんなんじゃ仕事できん!

  • ${PWD} が長い
    • プロジェクトを整理しようと階層的にしたら、長くなった
  • Python の仮想環境の名前が長い
    • poetry の設定をいじって、pyenv の仮想環境と同じように扱えるようにした
    • プロジェクトのディレクトリに行くだけで仮想環境に入れるので、poetry の仮想環境を pyenv と同様に扱うのは(私にとって)必要不可欠
    • 仮想環境の名前を出すのも、どの仮想環境にいるか一目で分かるので、必要不可欠
    • poetry が仮想環境名として unique ID 的なものや Python のバージョンを勝手に付けるので、poetry が悪い
      • でも poetry の仮想環境の名前の付け方を変更する方法は分からない

これまでの PROMPT の設定

こんな感じの設定にしてた。

autoload colors
colors
local p_cdir="%B%F{blue}[%~]%f%b"$'\n'
local p_info="%n@%m${WINDOW:+[$WINDOW]}"
local p_mark="%B%(?,%F{green},%F{red})%(!,#,>)%f%b"
PROMPT=" $p_cdir$p_info $p_mark "

一応解説する。

  • %B...%b
    • 囲まれている中身の文字を Bold にする
  • %F{color}...%f
    • 囲まれている中身の文字の色を color にする
    • color として使える名前は、black, red, green, yellow, blue, magenta, cyan, white
      • 頑張れば256色とかを指定できるけど、ここで頑張らないで color schema の設定を頑張る方針
  • %~
    • ${HOME} の下にいるなら ${HOME}~ に置き換えて、それ以外なら root (/)からの絶対パスで、${PWD} を表示する
  • $'\n'
    • 改行
      • ダブルクォーテーション(")だとダメ
  • %n
    • username
  • %m
    • hostname
  • ${WINDOW}
    • screen に入っていると、そのウィンドウ番号が入る
    • screen に入っていない場合は、定義されれない
  • ${HOGE:+moge}
    • ${HOGE} という変数に何か値が入っている場合、moge という文字列になる
    • ${HOGE} という変数に値が入っていない場合、そのまま空になる
  • (ちなみに)${HOGE:-moge}
    • ${HOGE} という変数が無いか空の場合に、moge という文字列になる
    • ${HOGE} という変数に値が入っている場合は、その値
  • %(?,hoge,moge)
    • 直前のコマンドの返り値($?)が 0 (正常終了)ならば、hoge
    • 直前のコマンドの返り値が 0 でない(異常終了)ならば、moge
  • %(!,hoge,moge)
    • ユーザーが root(id==0)ならば、hoge
    • ユーザーが root でないならば、moge

${PWD} が長い問題

これは %~ 自体に if else みたいな機能があるので、それを使えば簡単に設定できる。

  • %(5~|hoge|moge)
    • %~ で、表示されるディレクトリの数が5個以上だったら hoge、それ以外ならmoge になる
  • %(5~|.../%2~|%~)
    • %~ で、表示されるディレクトリの数が5個以上だったら、.../ という文字に続きディレクトリの最後の2つを表示、それ以外なら %~ を全部表示
  • %(5~|%-2~/.../%1~|%~)
    • %~ で、表示されるディレクトリの数が5個以上だったら、ディレクトリの最初の2つと、/.../ という文字、そしてディレクトリの最後の1つを表示

これで解決。あとは自分の好みに設定すれば良い。

Python の仮想環境名が長い問題

まず、PROMPT に何も設定していないのに、プロンプトに仮想環境名がついてしまっているのだが、これは pyenv の機能。いつもお世話になってます。助かってます。

今回はそれじゃダメなので、無効化する。

export VIRTUAL_ENV_DISABLE_PROMPT=1

これを設定しておけば良い。後は自分で設定する。

pyenv の仮想環境に入ると、${VIRTUAL_ENV} という変数に仮想環境名、正確には、仮想環境が置かれているディレクトリの絶対パスが入る。
例えば hogehoge という名前の仮想環境ならば、${HOME}/.pyenv/versions/hogehoge
(pyenv を ${HOME}/.pyenv にインストールした場合)

欲しいのは最後のディレクトリ名だけ。これを変数内の文字列置換でさくっと取り出そう。

変数内の文字列置換

置換したいなって時に毎回ググるので、ここでまとめてしまう。

  • 文字列先頭からの最短マッチで該当部分を削除
    • ${HOGE#pattern}
      • 使わない
  • 文字列先頭からの最長マッチで該当部分を削除
    • ${HOGE##pattern}
      • basename, 拡張子の抽出
  • 文字列最後からの最短マッチで該当部分を削除
    • ${HOGE%pattern}
      • dirname, 拡張子を取り除く
  • 文字列最後からの最長マッチで該当部分を削除
    • ${HOGE%%pattern}
      • 最も使わない
  • 例)HOGE="/hoge/moge/fuga.fuga.ext"
    • ${HOGE##*/}fuga.fuga.ext:basename
    • ${HOGE##*.}ext:拡張子の抽出
    • ${HOGE%/*}/hoge/moge:dirname
    • ${HOGE%.*}/hoge/moge/fuga.fuga:拡張子を取り除く
    • ${${HOGE##*/}%.*}fuga.fuga

cut と rev の活用

余談が長かったが、要するに ${VIRTUAL_ENV##*/} で仮想環境名が取れる。

しかしこの値は textcnn-gradcam-WPG28PJj-py3.9 のように poetry が勝手に付けた ID や Python バージョンが入っている。
これを取り除きたい。

poetry が仮想環境名として付ける形は (プロジェクトのディレクトリ名の _ を - に置き換えたもの)-(IDっぽいもの)-(Python バージョン) ということは分かっているので、「文字列を - で分割した最後の2つを取り除いたもの」を作れば良い。

分割すると言えば cut である。
例えば以下のようにすると、一見取り出せたかのように見える。

echo ${VIRTUAL_ENV##*/} | cut -d- -f-2textcnn-gradcam

cut-d- オプションで - を区切り文字とすると指定して、-f-2 オプションで区切られた文字列の最初から2つ目までを取得する(-f1-2 と同じ)。

しかしこれではダメだ。プロジェクトのディレクトリ名に _ もしくは - が何個入っているかは決まっていない。「最後から2つ」を取り除きたいのだが、cut には「最後から数える」という機能は備わっていない。

そこで rev である。rev は文字をひっくり返すコマンド。
だから、ひっくり返す → 最初から3つ目以降を取得 → またひっくり返す、とやれば良い。

echo ${VIRTUAL_ENV##*/} | rev | cut -d- -f3- | revtextcnn-gradcam

これで完璧。

更新するタイミング

しかしまだ終わりではない。

${WINDOW} ならば、screen で新しい WINDOW に入る時に .zshrc を読み込むので、ただ .zshrc${WINDOW} と書いておけば意図したとおりに設定される。

しかし今回はディレクトリを移動する毎に値を変更して欲しい。
.zshrc にただ書いただけでは、シェル(ターミナル)を起動した瞬間にしか読み込まれず、ディレクトリを移動してもプロンプトは変わらない。

そこで、ZSH の add-zsh-hook というモジュールをロードして、プロンプトが呼び出される前のタイミングの hook である precmd に関数をぶら下がらせて、その関数の中で PROMPT を更新させれば良い。

結果

最終的には、.zshrc のプロンプトの設定部分は以下のようになった。

export VIRTUAL_ENV_DISABLE_PROMPT=1
autoload -Uz colors
colors
autoload -Uz add-zsh-hook
local p_cdir="%B%F{blue}[%(5~|.../%2~|%~)]%f%b"$'\n'
local p_info="%n@%m${WINDOW:+[${WINDOW}]}"
local p_mark="%B%(?,%F{green},%F{red})%(!,#,>)%f%b"
local p_base=" $p_cdir$p_rhost$p_info $p_mark "
function __precmd_virtual_env() {
    local p_penv="%B%F{cyan}${VIRTUAL_ENV:+(`echo ${VIRTUAL_ENV##*/}|rev|cut -d- -f3-|rev`)}%f%b"
    PROMPT="$p_penv$p_base"
}
add-zsh-hook precmd __precmd_virtual_env

自分で設定したので、仮想環境名に色が付けられるようになった。

で、こうなる。

スクリーンショット 2021-10-11 1.48.26.png

とりあえず満足である。

まとめ

「俺はなんで未だに screen を使おうとしてるんだろう」と疑問に思ってきたので、tmux の設定をしたい気分になっている。(仕事はまだしてない)

7
5
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
7
5