えびちゃんの日記

えびちゃん(競プロ)の日記です。

Zsh を導入したよ

長いこと Bash を使い続けていました。 逆張り癖が災いしすぎた気がします。 Bash は man ページを一通り読むくらいには愛着を持っていたり、あれこれ触ったりしていました。

Zsh の導入自体は何度か挑戦しようとしていたのですが、(多くは勉強不足で)合わなさを感じてしまって投げ出していました。 先日ふと思い立ってまた触り始めてみて、挫折しないようにちゃんと調べながら触っていたところ、かなり快適になったのでメモしておきます。

導入

brew install zsh

とかをしたと思います。Mac 以外の人はよしなに。

カスタム

こんな感じになりました。

github.com

シェルをカスタマイズするとき、何から始めますか? えびちゃんは PS1 から始めます。

PS1

PS1 は、コマンドを入力する際の > 的な部分に表示させる文字列(prompt string)の一つです。 あんまりごちゃっとはさせたくないんですが、必要最低限のものは書かれていてほしいです。

  • 前の処理との間の空行*1
  • 前の処理の終了ステータス
  • シェルのバージョン情報と SHLVL
    • これはいらないかも
    • SHLVL はシェル中でシェルを起動すると増えるやつです
  • 停止中のジョブ数
  • カレントディレクト

かなりすごくて、大抵は ここ を見ると解決します。 条件分岐とか、太字や色などのハイライトも揃ってます。

終了ステータスは、こんな感じで表示させるやつです。

f:id:rsk0315:20200526203042p:plain

  • 値によって異なる文字列を出力したい
  • コマンドを入力しなかった場合は表示されないでほしい

という要望がえびちゃんの中ではあります。

毎回表示されてよくて、せいぜい色分けだけしてくれればいいというのであれば、次のような感じに書けばいいです。

PS1='%(?.%F{10}0%f.%F{9}%?%f)%# '

えーすごいね。

0 じゃなかったときだけ表示してくれればいいのなら、オプションで済むようです。

setopt print_exit_value

えびちゃんの設定については、記事を書きました

シェルオプション

setopt interactive_comments  # 対話モードでも # をコメントにする
setopt correct  # 訂正をお願いする
unsetopt correct_all  # お節介すぎるのはやめてほしい
setopt noautomenu  # 補完が一意じゃなかったら怒ってね
setopt histverify  # 履歴の展開は実行前に確認してね
setopt extended_history  # 履歴をいい感じに書いてね
setopt magic_equal_subst  # 補完をうまくやってね

magic_equal_subst、すごくて、

make --prefix=/path/to/

みたいなののパスも補完してくれます。

環境変数

export WORDCHARS=''  # 単語とみなす英数字以外の文字集合
export FIGNORE='~'  # 補完の際に無視する接尾辞
export HISTSIZE=100000  # 履歴に関するものたち
export SAVEHIST=100000
export HISTFILE=$HOME/.zhistory

キーバインド

以下では、よくある略記を使います。

  • ^X^Y or C-x C-y
    • control を押しながら x を押して、control を押しながら y を押す
  • ^Xz or C-xz
    • control を押しながら x を押して、control を離して z を押す
  • ^[w or \ew or M-w
    • esc を押したあと w を押す
    • option (meta, alt) を押しながら w を押す

control と同時押しされる文字は case-insensitive*2 ですが、option と同時押しの場合は case-sensitive っぽい挙動をするので気をつけましょう。

emacs-forward-word

デフォルトの M-femacs のように「単語の末尾へ移動」ではなくて「次の単語の頭へ移動」だったのが気に食わなかったので変えました。

bindkey '^[f' emacs-forward-word

option を押しながら「→」を押したときの挙動も変わります。

^[F と入力すると option+shift+f になるので注意しましょう。

pound-insert

M-# で、その行をコメントだったことにできます。

bindkey '^[#' pound-insert

expand-or-complete-prefix

cd Doc/neko のように書いてから、Doc の後にカーソルを持っていって TAB を押したとき、Doc.../neko のように補完されてほしいです。 デフォルトだと Doc/neko... のような補完をしてしまうらしいので、変えます。

bindkey '^I' expand-or-complete-prefix

digit-argument

emacsbash では、キーバインドを実行する際に引数を渡すことができます。 たとえば、a に 20 を渡すと 20 個の a を挿入できたりします。

挿入の方法としては、次のいずれかがあります。

  • M-2 0 a
  • M-2 M-0 a
  • C-u 20 a
  • C-u 20 C-u a
    • a ではなく 1 のような数字に引数を渡したい場合は C-u で引数部分の終了を示す
    • 数字以外なら任意

zsh では若干挙動が違っていて、M-2 0 と入力すると、0 に 2 を渡したことになるっぽいですね。 M-2 M-0 a のように渡す必要があるようです。

それから、入力中の引数が表示されてくれなくて不便だったので、自分で少し書いてみることにしました。

実装が長めなのでこれも後でどうにかするとして、デモだけ貼ります。

いつもの

いくつかのキーバインドは、Zsh 以外のキーバインドと衝突して、思うように使えないのです。 それを無効化してしまいます。

stty start undef  # '^q'
stty rprnt undef  # '^r'
stty stop undef  # '^s'
stty kill undef  # '^u'
stty werase undef  # '^w'

元通りに戻したいときは、undef の箇所を ^q などに置き換えたものを実行するとよいです。 その行を消してからターミナルを再起動してもいいと思います。

さて、自分のすきなのを割り当てます。

bindkey '^W' kill-region

ハイライト

これの存在を忘れていました。ここ に従います。

for k in ${(k)ZSH_HIGHLIGHT_STYLES}; do echo "ZSH_HIGHLIGHT_STYLE[$k]='${ZSH_HIGHLIGHT_STYLES[$k]}'"; done

などを実行すると、指定できる箇所がわかるので、適宜書き換えて実行します。

ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=#7F7F7F'
ZSH_HIGHLIGHT_STYLES[globbing]='fg=magenta'
ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=#7F7F7F'
ZSH_HIGHLIGHT_STYLES[arg0]='fg=white,bold'
ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=9'
ZSH_HIGHLIGHT_STYLES[comment]='fg=#7F7F7F'
ZSH_HIGHLIGHT_STYLES[path]='none'

オプションのハイライトって実質不可能で、文字列からだけでは -bar-bar を渡したのか、-b -a -r と等価なやつなのかわからないんですけどね。-b ar-bar が等価な場合とそうじゃない場合があったり。

ところで本来 -- 以降はオプションと見なされないんですが、これもオプションとして色がついちゃいます。むむー。

補完

partial-word completion

emacs のファイル名補完って便利で、

  • foo/bar-baz
  • foo/bad
  • food/basket
  • failed/bag
  • fish/big-neko

のようなファイルたちがあったとき、f/-n と入力してから TAB を押すと fish/big-neko に補完してくれたりするんですよね。

というのを、Zsh にもやってもらいます。

autoload -Uz compinit
compinit

対話モードで compinstall を実行して、2. Matching control を選ぶと出てくる p. Partial-word completion がそれっぽいんですが、それをしなくてもやってくれそう? ちょっとよくわからないです。

zstyle ':completion:*' matcher-list 'r:|[._-]=** r:|/=* r:|=*'
autoload -Uz compinit
compinit

こんな感じのを書くとよかったです。 a-b-c のようなファイル名に対しては -<TAB> で補完が効くのですが、ディレクトリに関してはそういう補完をしてほしくないので、/* にしています。 詳しくは ここ を読むといいです。

不満

ところで */-n とかだと補完されてくれません。ワイルドカードが混ざってると無理ってどこかで見たような気もします。 .//-n とかで補完するといいのかも。

単語の途中で補完した場合(上の補完の例で f/b と書いて TAB を押した場合などに発生する)に / が付加されないでほしくて、そういうオプションはあってほしいのですが、見つからないですね。 squeeze-slashes はちょっと違う気がします。squeeze されてほしくないときもあるので。

と思ったんですが、補完される / を無視して移動すると無くなってくれるので、とりあえずは我慢かなぁ。

git の補完

これ を見るといいです。 あれ

なんか手元で補完して出てくるのとサンプルが違ったので困ってました。

これ~/.zsh/completions/_git とかに置いておいて、

fpath=(~/.zsh/completions/_git $fpath)

とかするとよさそう。

ZLE

Zsh command line editor と呼ばれるやつです。

キーバインドで呼ばれる関数(ウィジェットとか呼ばれてそう)を自分で作ったり、キーを割り当てたりできます。

neko() {
    # したい処理を書く
    # zle -R '> ' みたいにプロンプトを書けたり
    # "$BUFFER" で行の内容を取れたり
    # zle -U "$key" みたいにして return 後に $key を入力したことにできたり
    # いろいろできる
}

サンプルは ここ とか見るといいかも。

関数を作ったら、キーバインドも設定します。

zle -N neko && bindkey '^Xneko' neko

SPROMPT

correct が関連してるんですが、間違ったコマンドを入力すると訂正してくれます。 そのときのメッセージもカスタマイズできます。

export SPROMPT='zsh: correct '\''%B%R%b'\'' to '\''%B%r%b'\'' ([n]/y/a/e)? '

これができるなら bck-i-search とか fwd-i-search の文字列も変更させてくれてもよくない? 変に略されてるのすきじゃないんですが...

github.com

ハードコードされているように見えるなぁ。

PROMPT_EOL_MARK

(デフォルトでそうなんですが)prompt_crprompt_sp が有効になっていると

echo -n neko

のような、改行で終わらないコマンドを実行すると末尾に % が付加されます。

この % を設定する変数です。デフォルトでは %F%S%#%s%f のようなのと等価だと思います。

おわり

ほぼ不満がなくなったのでこの辺でひとまず終わり。

*1:前の処理の末尾に改行がなかったら適当なマーカーがつくっぽい?

*2:大文字か小文字かを気にしない。case-sensitive は気にする。