zshを使い始めて最初に気になる点のうち、
プロンプト
これまでとは違うシェルを起動してまず目にするのがプロンプトで、
zshに乗り換えたときもそうした違和感を感じ、
その機能をフルに活かしている、
nprom () {
setopt prompt_subst
local rbase=$'%{\e[33m%}[%%{\e[m%}' lf=$'\n'
local pct=$'%0(?||%18(?||%{\e[31m%}))%#%{\e[m%}'
RPROMPT="%9(~||$rbase)"
case "$USER" in
yatex) PROMPT=$'%{\e[33m%}%U%m{%n}%%%{\e[m%}%u ' ;;
java) PROMPT=$'%{\e[36m%}%U%m{%n}%%%{\e[m%}%u ' ;;
*)
local pbase=$'%{\e[$[32+RANDOM%5]m%}%U%B%m{%n}%b'"$pct%u "
PROMPT="%9(~|$rbase$lf|)$pbase"
;;
esac
[[ "$TERM" = "screen" ]] && RPROMPT="[%U%~%u]"
}
nprom
複雑に見えるが、zshrc
にあるPROMPT='%m{%n}%% '
およびRPROMPT='[%'
と基本的に同じである。これにちょっとした味付けを行なっている。この設定にしたときの端末で操作した様子を模したものをまず示そう。
firestorm{yuuji}% ] firestorm{yuuji}% ] firestorm{yuuji}% ls /usr ] X11R6/ include/ local/ share/ tools.i386/ X11R7/ lib/ lost+found/ spool/ x11r6.tar.gz bin/ libdata/ mdec/ src/ xsrc/ emul/ libexec/ pkg/ tests/ games/ lkm/ sbin/ tmp/ firestorm{yuuji}% ls /usr/hoge ] ls: /usr/hoge: No such file or directory firestorm{yuuji}% ] firestorm{yuuji}% cd /usr/src/external/bsd/openldap/dist/servers/slapd ] firestorm{yuuji}% [/usr/src/external/bsd/openldap/dist/servers/slapd] firestorm{yuuji}% cd back-bdb [/usr/src/external/bsd/openldap/dist/servers/slapd/back-bdb] firestorm{yuuji}%
要点を示すと以下のようになる。
- プロンプトに色付け
コマンドの出力が多くてさかのぼって見るときなど、
その出力がどこから始まっているのか判別しやすいよう太字にして色を付けている。 プロンプト文字列に文字属性を変えるエスケープシーケンスを
%{
と%}
で括って入れておけばよい。エスケープシーケンス先頭のESC文字は、zshでは $'…'
でクォートした\e
で表現できる。- 数式展開
-
以前遊びで入れた設定で
「zshっぽく見える」 効果しかないが、 zshでは数値演算や乱数生成ができるので色をランダムで変えている。数式展開ほか、 さまざまな展開をプロンプト文字列に対して施したい場合には setopt
でシェルオプションprompt_
を有効化しておく。subst - 右プロンプト
右側のプロンプト(
RPROMPT
)に現在ディレクトリを出しておく。あまりに階層が深くなった場合は右に出すのをやめて2行にする。これを切り替えているのが%n(~|string1|string2)
という記法で、スラッシュ区切りがn個以上の場合に string1 を、 そうでないときに string2 を設定する。上記設定例では、 PROMPT
、RPROMPT
ともにn=9での場合分けを行ない、スラッシュが9個未満のときは右プロンプト、 9個以上のときは左プロンプトに改行つきでパス名を出すようにしている。 - エラーの明示
直前に実行したコマンドが正常終了せず、
終了値 ( $?
)が0以外になったときはプロンプト末尾の %
を赤にしている。ただし、あるプロセス起動中C-zを押しsuspendしてコマンドラインに戻った直後は $?=18
となるが、suspend直後は赤でないほうがいいので $?=18
の場合も赤くしないよう除外している。この切り替えは
%n(?|string1|string2)
の記法で行なう。終了値がn
のときはstring1を、そうでないときはstring2 を出力する。 なお、
起動したコマンドがエラー終了したかを知るにはシェルオプション print_
をセットしておくのも効果的で、exit_ value この場合はエラー終了したときのみ、 その終了値を出力してくれる。
筆者個人の設定ではカラフルにしているが、
RPROMPT='(%~)'
PROMPT=$'%B%m%b:%?:%# '
スーパーユーザでは色を付けず一般ユーザ時と違うことを意識させつつ、%?
は直前のコマンドの終了コードの値をそのまま出す表記である。
ここまでの例にあるように、%
で始まる記法を様々な文字列に展開する。代表的なものを下記に示しておく。
記法 | 意味 |
---|---|
%% | % 文字自身 |
%# | 一般ユーザなら % 、# |
%l | tty名 |
%M | ホスト名 |
%m | ホスト名 |
%n | ユーザ名 |
%? | 直前のコマンドの終了値($? ) |
%/ | カレントディレクトリ |
%~ | 同上。ただし~ 記号などで可能な限り短縮する |
%! | ヒストリ中のイベント番号 |
%D{FMT} | strftime(3)関数にFMTの書式を渡したときの現在時刻の文字列 |
%B | 太字開始 |
%b | 太字解除 |
%U | 下線開始 |
%u | 下線解除 |
%S | 強調開始 |
%s | 強調解除 |
%{…%} | 生のエスケープシーケンスを挟む |
%n>string> |
以降のプロンプト文字列の最大長をn文字以下に後略表記する。省略を示す文字列としてstringを使う。 |
%n<string< |
以降のプロンプト文字列の最大長をn文字以下に前略表記する。省略を示す文字列としてstringを使う。 |
最後の%n<string<
は、%~
などを設定してパス名を出しているときに、
たとえば、
RPROMPT='[%39<...<%'
すると、
duke{yuuji}% pwd ] duke{yuuji}%] duke{yuuji}% cd /usr/src/sys/arch/amd64/compile/DUKE ] duke{yuuji}% [/usr/src/sys/arch/amd64/compile/DUKE] duke{yuuji}% cd lib [...src/sys/arch/amd64/compile/DUKE/lib]
プロンプト中の%
記法には、%
の直後に整数を付加して、
キーカスタマイズ
シェルを手に馴染ませるために比較的簡単にできる設定の1つがキーカスタマイズである。そのための要点を示しておこう。
bindkeyコマンドとキーマップ
zshのキー割り当てはbindkey
コマンドで行なう。zshには複数のキーマップがあり、
% bindkey -e
vi風の操作感を好む場合は、
% bindkey -v
実はzshではさらに違うユーザ独自の新たなキーマップを作ることもできるし、
# 新しいウィジェットとなる関数 365march-forward() を定義
function 365march-forward () {
: ${_365march:-0}
if ((_365march++ % 4)) then
zle forward-char
else
zle backward-char -n 2
fi
}
# この関数をウィジェット化
zle -N 365march-forward
# emacsキーマップを見本に新しいキーマップ365marchを作る
bindkey -N 365march emacs
# 365marchキーマップの C-f に 365march-forward を割り当てる
bindkey -M 365march '^f' 365march-forward
# 使用するキーマップを 365march に変える
bindkey -A 365march main
この状態でコマンドラインに何文字か打って行頭に戻り
% bindkey -e
かくも過激な機能拡張ができるzshだが、
ヒストリ呼出しのカスタマイズ
シェルを使い始めの頃は、
そのためには検索機能つきで古いコマンドラインに戻る機能を用いればよい。その検索方法には大別して2つがある。
- 行頭マッチの検索
- インクリメンタル検索
(EmacsでいうC-s、C-r)
zshのemacsキーマップのデフォルトではC-p、C-nに検索なしの機能が割り当てられているが、
bindkey '^p' history-beginning-search-backward
bindkey '^n' history-beginning-search-forward
これは、
grep foo /usr/local/apache/logs/access_log
tail -f /usr/local/apache/logs/error_log
gimp &
コマンド入力履歴が上記のような場合に、grep
まで入力してhistory-beginning-search-backward
を呼び出すとカーソル位置は以下のようになる。
% grepC-p (C-pには history-beginning-search-backward が割り当てられている) ↓ % grep foo /usr/local/apache/logs/access_log ↑カーソル位置(pの次)
カーソルが行末に来る挙動に慣れていると最初はぎょっとするが、grep
の検索パターンを変えて何度も試したりするにはカーソル位置がその場の方が好都合だ。状況によるので一概にどちらが有利不利とは言えない。ただ、C-e
ですぐ行けるものの、
どうしても行末にカーソルが来た方がよい場合は別の機能を割り当てるとよい。
autoload -U up-line-or-beginning-search down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey '^p' up-line-or-beginning-search
bindkey '^n' down-line-or-beginning-search
エディタ(vi)とコマンドラインの往復
tcshの行エディタにある便利な機能にrun-fg-editor
というものがある。これは、
vi | C-z → ← C-z |
コマンドライン |
これと同じことをzshで行なうには、bindkey -s
で登録する。C-zに割り当てる例を示す。
setopt hist_ignore_space
bindkey -s '^z' '^[q %vi^m'
これは、C-zを押したときに、" %vi"
を挿入し、C-mをタイプした」%vi
はシェルの管理下にあるジョブのうち、viという文字列で始まるジョブをフォアグラウンドにすることを意味し、hist_
有効時)。
ちなみに筆者自身はEDITOR
に登録されているエディタ」
export EDITOR=vim
alias vi='$EDITOR'
bindkey -s '^z' '^[q %\\$EDITOR^m'
もっと bindkey -s を
tcshでも全く同様に使える機能だが、bindkey -s
は、
シェルを主な作業場所としている人であれば、
- ファイルを展開するために
「 "gtar zxpf "
」 - 出力結果をページャで見るために
「行末に移動して "| less"
をタイプしてリターン」 - 出力結果の行数を調べるために
「行末に移動して "| wc"
をタイプしてリターン」 - 出力を捨てるために
「 ">/dev/
」null" あるいは 「 ">&/dev/
」null" - 何かの出力の第Nフィールドをawkで取り出すために
「 " | awk '{print $N}' | "
」(Nはその都度変わる) - カレントディレクトリ以下すべての通常ファイルを指定するために
「 "**/*(.)"
」
このように、bindkey -s
で目の前にすぐ取り出せるようにしておくと効果的で、"gtar zxpf "
が出てくる。
bindkey -s '^[G' 'gtar zxpf '
また、"| awk '{print $}'|"
が挿入され、$
の直後になる。
bindkey -s '^[A' "| awk '{print $}'|^B^B^B"
よく使うless
は以下のようにしておくとよいだろう。
bindkey -s '^[L' "^E|less^M"
ちなみにbashの場合は、~/.inputrc
に記述する。
$if Bash
"\ep": history-search-backward
"\en": history-search-forward
"\eG": "gtar zxpf "
"\eL": downcase-word
"\el": "\C-e|less\C-m"
$endif
割り当てるキーの選択
bindkeyで
自分仕様のものをキーに割り当てるとき、down-case-word
で、"^E|less^M"
のよく使う方をESC-l、less
起動の方をよく使うため、
よりたくさんのキーを割り当てたいなら、
たとえば筆者は独自のキー割り当てを多数持ちたいので、
# Ctrl-Q prefix
bindkey '^q ' set-mark-command
bindkey '^q^b' vi-history-search-backward
bindkey '^q^d' end-of-line
bindkey '^q^e' up-line-or-beginning-search
bindkey '^q^n' down-line-or-beginning-search
bindkey '^q^f' history-incremental-search-backward
bindkey '^q^h' backward-kill-word
bindkey '^q^i' expand-or-complete-prefix
bindkey '^q^j' exchange-point-and-mark
bindkey '^q^s' beginning-of-line
特別な宣言なしに2ストローク以上のキーを指定しても、end-of-line
を呼び出してくれる。さらに、C-qだけをタイプして一定時間$KEYTIMEOUT
)push-line
)
このように、
% ls / ↑ここで M-x beginning-of-line % ls /
機能名はEmacsと同じ慣習で付けられている。機能名の入力時には、
まとめ
プロンプトもキー割り当ても何度も触れるものだけに、bindkey
コマンドを引数なしで起動して得られるものだけでなく、
次回は