2006年08月04日(金)
■[zsh] bash -> zsh 移行レポート
●背景
春くらいに zsh がマルチバイト文字に正式対応したので、zsh に移行しない理由は何もなくなったので、思いきって zsh に移行してみた。
●マニュアル
zsh は長い歴史のある素晴しいソフトなんだが、全然普及しないのは、ドキュメントの出来がひどいからだろう。
仕様集という感じでまとまっておらず、デンマーク人が書いているからか、とても英語とは思えない個所も多々あり(俺なんからヒトのことはいえんが)とにかく読みにくい。
作業に入る前に三日程眺めたが、zsh に関しては、マニュアルは読むものではなく、リファレンスマニュアル的に引くだけのものと考えた方がいい。
●結論
結論からいうと、移行作業は、拍子抜けするくらい楽だった。こんなことだったら、もっと早くに移行すべきだった。
- .bash_profile -> .zprofile は実質的な変更個所なし
- .zshrc は初回起動時の設定支援ソフトが生成する記述に、.bashrc の記述を適宜足すだけ
- 通らなかったのは、プロンプト文字列(のエスケープシーケンス)だけ
- Ver4 の compsys は、デフォールト設定そのままでもびっくりする程賢い
● .zshrc の設定
○ compinstall の作ってくれる設定雛形
初回起動時に、compinstall が、対話的に .zshrc の雛形をつくってくれる、ここは、特に気になるものがなければ、キーバインド以外は全部デフォールトでいいんじゃないかと思う。
僕の場合は、下のようなものを作ってくれた。
# Lines configured by zsh-newuser-install HISTFILE=~/.histfile HISTSIZE=10000 SAVEHIST=10000 bindkey -e # End of lines configured by zsh-newuser-install # The following lines were added by compinstall zstyle :compinstall filename '/home/amt/.zshrc' autoload -Uz compinit compinit # End of lines added by compinstall
これだけで、 Ver 3 までは大変だった、補完設定もけっこう使えるようになる。
○ オプションの設定
僕は マニュアルのオプションの項目 を眺めながら、とりあえず気になるものを設定してみた。
広瀬雄二さんのお薦めが ここ にあるので、僕はそれも参考にした。
###
# Set shell options
###
# 広瀬雄二さんのお薦めオプション
# http://www.gentei.org/~yuuji/rec/pc/intro-zsh.html
# setopt auto_menu auto_cd correct auto_name_dirs auto_remove_slash
# setopt extended_history hist_ignore_dups hist_ignore_space prompt_subst
# setopt pushd_ignore_dups rm_star_silent sun_keyboard_hack
# setopt extended_glob list_types no_beep always_last_prompt
# setopt cdable_vars sh_word_split auto_param_keys
# 以下、広瀬レコメンドは小文字、そうでないのは大文字にしてある
setopt auto_cd # コマンドが省略されていたら cd とみなす
setopt AUTO_PUSHD # cd 時にOldDir を自動的にスタックに積む
setopt correct # コマンドのスペルチェック
setopt auto_name_dirs # よく判らん
setopt auto_remove_slash # 補完が/で終って、つぎが、語分割子か/かコマンド
# の後(; とか & )だったら、補完末尾の/を取る
setopt extended_history # ヒストリに時刻情報もつける
setopt extended_glob # グロブで、特殊文字"#,~,^"を使う、
setopt FUNCTION_ARGZERO # $0 にスクリプト名/シェル関数名を格納
setopt hist_ignore_dups # 前のコマンドと同じならヒストリに入れない
setopt hist_ignore_space # 空白ではじまるコマンドをヒストリに保持しない
setopt HIST_IGNORE_ALL_DUPS # 重複するヒストリを持たない
setopt HIST_NO_FUNCTIONS # 関数定義をヒストリに入れない
setopt HIST_NO_STORE # history コマンドをヒストリに入れない
setopt HIST_REDUCE_BLANKS # 履歴から冗長な空白を除く
setopt MULTIOS # 名前付きパイプ的に入出力を複数開ける
setopt NUMERIC_GLOB_SORT # グロブの数のマッチを辞書式順じゃなくって数値の順
setopt prompt_subst # プロンプト文字列で各種展開を行なう
setopt no_promptcr # 改行コードで終らない出力もちゃんと出力する
setopt pushd_ignore_dups # ディレクトリスタックに、同じディレクトリを入れない
#setopt rm_star_silent # rm * とかするときにクエリしない
#setopt no_beep # ZLE のエラーでビープしない
#setopt cdable_vars # cd の引数のdir がないとき ~をつけてみる
setopt SHARE_HISTORY # 複数プロセスで履歴を共有
setopt SHORT_LOOPS # loop の短縮形を許す
setopt sh_word_split # よく判らん
setopt RC_EXPAND_PARAM # {}をbash ライクに展開
setopt TRANSIENT_RPROMPT # 右プロンプトに入力がきたら消す
# Ctrl-D でログアウトするのを抑制する。
setopt ignore_eof
# グロブがマッチしないときエラーにしない
# http://d.hatena.ne.jp/amt/20060806/ZshNoGlob
setopt null_glob
# デバッグ用 コマンドラインがどのように展開されたか表示
#setopt xtrace
# 小文字に対して大文字も補完する
# http://www.ex-machina.jp/zsh/index.cgi?FAQ%40zsh%A5%B9%A5%EC#l1
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
○プロンプト文字列
上述のように、.bashrc 上の記述を持ってきて、そのまま素直に動かなかったのはプロンプト文字列中に使っていたエスケープシーケンスだけ。
とりあえず下のようにしてみた。
ちなみに僕はマシン毎に前景色、端末毎に背景色を使い分けているのだが、前景が fg で、色の名前を cyan とか、コードじゃなくってヒューマンリーダブルな言葉で設定できるのはとても助かる。
プロンプトについても凝りたかったら、マニュアルの プロンプト展開の項 は読む値打ちがある。
# プロンプトの設定
case "$TERM" in
dumb*|emacs*) # ダム端末では色を出さないし、右プロンプトも出ない
PROMPT="%m[%h]%# "
#RPROMPT=" [%(5~,%-2~/.../%2~,%~)]${WINDOW:+"[$WINDOW]"}"
;;
*)
autoload -U colors ; colors
PROMPT="%{$fg[cyan]%}%m[%h]%#%{$reset_color%} "
RPROMPT="%{$fg[cyan]%}[%(5~,%-2~/.../%2~,%~)]${WINDOW:+"[$WINDOW]"}%{$reset_color%} "
;;
esac
- Phil!'s ZSH Prompt
- zsh プロンプトの偏執的な設定を紹介しているページ、とにかく凄い
- いやなブログ: 続: zsh のプロンプトでカレントディレクトリを短縮表示
あと、screen のウインドウ名をダイナミックに抽出するのに screen の info に書いてある手順だと、プロンプトにそれ用のエスケープシーケンスを仕込むんだけれど、これも動かなくなった。
これも、上述の Phil!'s ZSH Prompt で示されている方法を以下のようにそのままパクって回避。
いちいち解説しないけど、これってとっても巧妙なコード
preexec () {
if [[ "$TERM" == "screen" ]]; then
local CMD=${1[(wr)^(*=*|sudo|-*)]}
echo -ne "\ek$CMD\e\\"
fi
}
○ディレクトリスタック
シェルのディレクトリスタックって、そのままじゃ使いものにならんユーザインタフェースだと思うけれど、 zsh は、オプション設定でディレクトリ移動毎にスタックに自動的にプッシュしたり、重複するものはかたずけたりする機能がある。
それに加えて、ここに、ディレクトリスタックを表示して、その中から選んでディレクトリ移動するスクリプトがある、これらを組み合わせると、ディレクトリスタックも、ちゃんと使いものになるものになる。
#20060803 # ディレクトリスタックからディレクトリを選ぶ #http://www.ayu.ics.keio.ac.jp/~mukai/tips/zshdir.html alias gd='dirs -v; echo -n "select number: "; read newdir; cd +"$newdir'
●その他の修正
僕の環境の場合 screen + zsh + emacs -nw で emacs の日本語が文字化けしてしまった。
下のように、terminal-coding-system を明に設定してやると解決
(set-terminal-coding-system 'utf-8-unix)
●感想など
zsh は、コマンドラインインタプリタとして決定版であるばかりでなく、プログラミング言語としても面白い存在である。
連想リストも使えるし、正規表現とは違った方向に拡張されたグロブもつかえるし、プロファイラまである。
このブログでも、これから晒してみる機会があると思う。
●不具合記録
※ 20060706 追記
使っていると、矢張り自作シェルスクリプトや .zshrc 中のシェル関数なんかで、次々と微妙な不具合がでてきた。
○マッチするパターンがない時のグロブの動作
20060806の日記に詳しく書いたけれど、なんと恐しいことに、zsh はデフォールトの状態で、グロブのファイル名展開のときに、マッチするファイルがないと失敗する。どういうことかというと、↓のようにした場合、*.nofile にマッチするファイルがないと、ls に渡る前、グロブ展開した瞬間にエラーになって終了してしまう。だからエラーメッセージも /dev/null にいかずに、↓のようにしてもエラーメッセージも表示される。
で、だ、スクリプト中にグロブ展開して空振り(マッチするものがなかったら)、そこでエラーを起してスクリプトが停止してしまうんだ。(これって俺にはとっても難解なバグだった)
% ls *.nofile 2> /dev/null
この問題は、setopt nonomatch してやると sh/bash 同様の動作になってくれるんだが、このオプションも、オプションのネーミング規約からすると反則なんだな。
この問題は、zsh は、オプション設定によって非常に大きく動作が変ってしまうということも教えてくれる。だから、配布を想定している zsh スクリプトの先頭行には絶対↓のように -f(NO_RCS /etc/zshenv 以外の設定ファイルを読まない) が必要だ、ということが理解できた。
#! /usr/bin/zsh -f
○ 引数の有無の判定条件
僕は bash では、引数の有無の判定をするときに次のように if 条件部に $* を直接置いていた。
hatena () {
if $*
then /usr/bin/w3m "http://d.hatena.ne.jp/amt/"
else
/usr/bin/w3m "http://d.hatena.ne.jp/amt/archive?word="$*"&hl=ja"
fi
}
bash では、これは僕の期待とおり、引数有無判定として動作する。が、これが正しいかどうかは、あまり深く考えたことがなかった。たまたま動いていたから使っていた訳だが、後述のようにちゃんと書くよりラクだから僕はこれを多用していた。(シェルは文法的に曖昧なことが多いので、、自家消費するスクリプトでは、このように深く考えずに適当に結果オーライでやっている個所は沢山あったりする。)
だが、zsh では、$* が展開された後、シェルコマンドとして実行される。!
だから、bash 上で hatena zsh としたら、コマンドインタプリタがあらわれるので、一見スクリプトが何もせずに終了したように見える。
↓のように、ちゃんと書くとちゃんと動く。
hatena () {
if [ $# -gt 0 ]
then /usr/bin/w3m "http://d.hatena.ne.jp/amt/"
else
/usr/bin/w3m "http://d.hatena.ne.jp/amt/archive?word="$*"&hl=ja"
fi
}
教訓、理解もせずに動くだけのコードで済ましていると、結局高くつく。
- 94 http://mixi.jp/view_bbs.pl?id=9195535&comm_id=291839
- 59 http://www.google.co.jp/search?hl=ja&client=firefox-a&rls=org.mozilla:ja-JP:official&hs=Tmy&q=zsh+auto_menu&btnG=Google+検索&lr=
- 44 http://www.google.co.jp/search?q=zsh+スクリプト&start=0&hl=ja&lr=lang_ja&ie=utf-8&oe=utf-8&client=firefox&rls=org.mozilla:ja-JP-mac:official
- 44 http://www.google.co.jp/search?sourceid=navclient&hl=ja&ie=UTF-8&rls=RNWE,RNWE:2004-37,RNWE:ja&q=液晶テレビ 複数チューナー
- 42 http://www.google.co.jp/search?hl=ja&q=シェル+コマンド resort&lr=
- 40 http://www.mars.dti.ne.jp/~hirok/diary/
- 32 http://72.14.235.104/search?q=cache:yYZD3P_echEJ:d.hatena.ne.jp/amt/20060804/zsh+.zshrc ディレクトリ 色&hl=ja&gl=jp&ct=clnk&cd=10&lr=lang_ja
- 25 http://www.google.co.jp/search?q=zsh+プロンプト+設定&hl=ja&lr=&start=10&sa=N
- 25 http://www.google.co.jp/search?sourceid=navclient-ff&ie=UTF-8&rls=GGGL,GGGL:2005-09,GGGL:ja&q=keyconfig+firefox
- 24 http://www.google.com/search?hl=ja&lr=lang_ja&ie=UTF-8&oe=UTF-8&q=コピーワンススルー+キャプチャ&num=50



