知ったらもう戻れない!? 便利すぎるZshの世界

Zshとは

ZshではUnix系OSで利用可能なシェルです。

Bashと同様、プロプライエタリだったksh(Korn Shell)の代替実装として作られたものですが、積極的かつ大幅な強化が行われています。

Zshのドキュメントのイントロダクションには次のように書かれています。

Zsh is a UNIX command interpreter (shell) usable as an interactive login shell and as a shell script command processor. Of the standard shells, zsh most closely resembles ksh but includes many enhancements. It does not provide compatibility with POSIX or other shells in its default operating mode: see the section Compatibility.

(Zshは対話的ログインシェル、あるいはシェルスクリプトプロセッサとして利用できるUNIXコマンドインタプリタ(シェル)です。標準的なシェルの中では、Zshはkshに似ていますが、多くの拡張が含まれています。デフォルトの動作モードではPOSIXや他のシェルとの互換性はありません: 互換性のセクションを参照してください。)

Bashはkshの特性や機能を踏襲していません。Bashの場合、基本的にはPOSIX shellの実装であると言うことができます。そのPOSIX ShellはBourne shellとkshの中間のような存在です。kshが出来た後に制定されましたから、ksh由来の仕様も含まれていますが、kshの機能、例えばksh globなどは含まれていません。

一方、Zshはkshの機能を網羅しつつ、より大幅な強化を行ったシェルです。 対話的機能においては多段階層補完やメニュー補完、インクリメンタル履歴検索など、スクリプト機能では様々なモジュールなど、どちらの面でも非常に強力なシェルとなっています。

通常、Zshは「BashかZshか」のように考えるものではありません。 LinuxにとってはBashは必須のシェルであり、基本部分を担うものです。 一方、Zshはユーザーにとって強力なツールとなるように設計された、追加のソフトウェアです。

ZshのライバルとしてはFishが挙げられることもあります。 Fishと比べ、Zshはその強力さに特化していると言うことができるでしょう。Fishの優れた機能とされるものも、Zshのプラグインとして利用可能であるものもあります。

デフォルトシェル

現在のシステムにおけるデフォルトシェルは次の通りです。

システム デフォルトシェル
Linux Bash
FreeBSD sh (FreeBSD sh)
NetBSD sh (NetBSD sh)
OpenBSD ksh (OpenBSD ksh)
Mac OS X -

これはシステムワイドでデフォルトなシェルという意味であり、デフォルトのユーザーシェルという意味ではありません。

ユーザーシェルという点では、Mac OS Xはtcsh, Bash, Zshと変遷しており、FreeBSDはルートのシェルとしてtcshを使います。

Mac OS Xは標準システム上でUnixシェルをインタープリタとして要求するようになっていないため、システムワイドなデフォルトシェルを持たないようです。

現状、システムシェルとしてZshを使うシステムはありません。 Zshは追加のソフトウェアである、と考えて良いでしょう。

Gentoo Linux、及びその派生ディストリビューションはユーザーシェルとしてZshを標準採用する傾向があります。 また、Manjaro Linuxも一時期Zshをユーザーシェルとしていました。

Zshの導入

ZshはLinuxディストリビューションでは一般的にzshパッケージによって導入できます。

pacman -S zsh

MSYS2においてもPacmanを使い、Zshを導入することができます。

Mac OS XはCatalinaにおいて、Zshを標準採用しました。 Mac OS XのBashはバージョンが古いままに置かれていることもあり、いくつか問題があります。その点においても、特にMac OS Xでは積極的にZshを使う理由があると言って良いでしょう。

標準でZshを使うようにするには次のようにします。 (/bin/zsh にあると仮定しています)

chsh /bin/zsh

このまま zsh -l とすればZshの世界を始めることができますが、ほとんどの場合これでは非常に使いにくいシェルであると感じることでしょう。Zshを使うためには設定が必要です。 少なくとも、compinitをしなければZshの実力は発揮されません。

grml-zsh-config

The Zsh Manualは非常に長大かつ難解なドキュメントです。 これを読み解くには相当な実力が必要となるでしょう。

しかし、適切な設定を施さねばZshの力は発揮できません。ここで登場するのがgrml-zsh-configです。

GrmlはDebian/GNU LinuxをベースとしたLive-CD Linuxディストリビューションです。 そのGrmlが採用するZshの設定が非常によくできていることから、Zshの設定として人気があります。 そのことも踏まえて、GrmlのZsh設定は単体で公開されています。

Arch Linuxでは grml-zsh-config パッケージがあり、これを導入することで /etc/zsh/zshrc として保存されます。

システム全体に対してgrml-zsh-configを設定するには次のようにします。

% sudo wget -O /etc/zsh/zshrc https://git.grml.org/f/grml-etc-core/etc/zsh/zshrc
% wget -O .zshrc https://git.grml.org/f/grml-etc-core/etc/skel/.zshrc

ユーザーローカルに設定したい場合は次のようにします。

% wget -O .zshrc https://git.grml.org/f/grml-etc-core/etc/zsh/zshrc
% wget -O .zshrc.local https://git.grml.org/f/grml-etc-core/etc/skel/.zshrc

これだけでもZshは超強力なシェルに変貌するはずです。

grml-zsh-configは標準でVCSにも対応した優れたプロンプトを持ちます。 しかし、もしもっと違うデザインのプロンプトにしたい場合も話は簡単です。

grml-zsh-configではpromptという関数が定義されています。 prompt -lとすることで利用可能なテーマを一覧することができます。そして、例えばprompt adam2とすればadam2テーマに切り替わります。

prompt offとすればこのプロンプト機能が無効になります。 カスタムしたプロンプトを使いたい場合はprompt offとした後に続けて書いてください。

Zshの機能紹介

メニュー補完

補完時に複数の候補がある状態でTABキーを再度押すと、項目がハイライトされ、カーソルキーによって補完項目を選ぶことができます。

この機能は AUTO_MENU, MENU_COMPLETE オプションによって有効になり、デフォルトでAUTO_MENUが有効です。

なお、デフォルトでは選択肢が4つ以上である場合にメニュー補完となります。 メニュー補完でなくてもタブを連打することで項目を選択することができますが、常にメニュー補完してほしい場合、

zstyle ':completion:*' menu yes select

とすれば選択肢が2つの場合でもメニュー補完となります。

コンテキスト補完

Zshは標準で今のカーソル位置に適切な補完を行うことができます。 次の例ではGitのサブコマンドを補完しています。

これは、Zshの補完機能がプログラマブルだからです。 かなり難しいですが、自分で補完を書くこともできます。

このプログラマブルな補完機能を有効にするには、compinit関数を呼び出す必要があります。 compinit関数を有効にするには、autoload -U compinitとします。

多段階層補完

Zshではディレクトリを再帰的に補完することができます。 次の例では gen//desGeneral/pbsimply-testsite/dest/ に補完しています。

複数候補がある場合、(GLOB_COMPLETEを有効にすれば)全体をメニュー補完の対象にすることもできますし、曖昧になっている階層から確定することもできます。

この機能はcompinitによって有効になります。

ZLE

Zshのラインエディタ、ZLEはマルチラインの編集が可能です。

forなどを使ってコマンドが何行にも渡るとき、カーソルで行を移動しながらコマンドを編集することができます。 キーバインドはEmacs, Viの2種類から選ぶことができます。

ZLEを使うにはZLEオプションを有効にしますが、これはデフォルトでなっています。

履歴検索: コマンド維持

履歴を辿るとき、そのコマンドの履歴だけを辿ることができます。 次の例ではコマンドで履歴を辿るとき、カーソルの位置は終端にしています。

これはZLEのhistory-search-backward 及び history-search-forward です。

こうした振る舞いのない、単純な履歴を辿るZLEコマンドとしては、 up-historydown-history があります。

履歴剣先: カーソル位置維持

history-beginning-search-backword 及び history-beginning-search-forward はカーソル位置を維持しつつ、カーソル位置までが一致する履歴を探索するものです。 例えば.zshrcにおいて次のようにすることで

bindkey "\e[5~" history-beginning-search-backward
bindkey "\e[6~" history-beginning-search-forward
bindkey -M viins "\e[5~" history-beginning-search-backward
bindkey -M viins "\e[6~" history-beginning-search-forward
bindkey -M vicmd "\e[5~" history-beginning-search-backward
bindkey -M vicmd "\e[6~" history-beginning-search-forward

PageUp/PageDownキーを使ってこの動作ができます。

インライン展開

変数、ヒストリ、グロブなどを入力したあと、TABキーを押すことでその場で展開してしまうことができます。

履歴の使い分け

Zshのfcコマンドはヒストリ機能に関するコマンドです。

fc -lでヒストリを一覧することができます。これはhistoryコマンドと同じです。

特に注目すべきは fc -p 及び fc -P です。

fc -pはヒストリスタックに新しいヒストリを積みます。このときファイルを指定するとそのファイルをヒストリファイルとして利用します。 これによって作業コンテキストによってヒストリを使い分けることが可能で、Zshの強力なヒストリ検索と合わせて非常に良好な使い勝手が得られます。

fc -pを引数なしで実行すると匿名のヒストリを積みます。 これは、コマンドを実験したい場合など、ヒストリを汚したくないときに便利です。

fc -Pはヒストリスタックからひとつヒストリを取ります。

次の例ではfc -lによっていくつものコマンド履歴があり、fc -pでそのコマンド履歴が新しいものとなり、fc -Pで元に戻っていることが確認できるでしょう。

再帰グロブ

**/はZshで常に利用できるグロブです。

これは再帰的なディレクトリを意味します。 次の例ではディレクトリレベルによらずLinuxで始まるファイルが補完されていることがわかるでしょう。

拡張グロブ

EXTENDED_GLOBが有効であれば、さらなる強力なグロブが利用可能になります。これは#, ~, ^の3つの記号がグロブとして有効になることを意味します。

#はより複雑なグロブのために使いますが、対話的シェルではあまり頻繁に使うものではありません。

^は否定グロブになります。例えば(^(foo))とすれば、カレントディレクトリのfoo以外のファイルに展開されます。(^(foo|bar))としてfooまたはbarファイル以外とすることもできます。

~は除外グロブです。例えば **/appl* で再帰的にapplで始まるファイルを探しますが、 **/appl*~**/application*~**/orange/* とすると、その中からapplicationで始まるファイルや、そのファイルが所属するディレクトリがorangeであるファイルは除外します。

(#i)はグロブをcase-insensitiveにするものです。

数値グロブ

ファイルを数値範囲で指定したい場合は多いものです。 Zshなら<n-m>というグロブで数値範囲を指定できます。これは、頭に0があってもなくても動作します。

角括弧によるグロブは桁が揃ってないと難しいですが、非常に簡潔に書けます。

NUMERIC_GLOB_SORTが有効であればファイルは数値順に展開されますが、一般的には有効にしていないでしょう。しかし、glob qualifierの(n)と組み合わせることでその場だけ数値順に並べることもできます。

glob qualifier

glob qualifierはグロブによって展開された対象から条件にあるファイルに限定するものです。

glob qualifierはリンクの数、パーミッション、ファイルサイズ、更新日時、ファイルの属性など非常に幅広い項目から細かく指定できます。特に頻繁に使うのは「通常ファイル」を示す .、「ディレクトリ」を示す /、「シンボリックリンク」を示す @、「空でないディレクトリ」を示す Fです。

glob qualifierを使うには、BARE_QUALオプションを有効にして(.)のような形で使うか、EXTENDED_GLOBを有効にして(#q.)のような形で使います。

修飾子

修飾子はBashでもヒストリには存在する機能ですが、Zshの場合変数展開でも有効です。

これは非常に便利です。修飾子には変換、置き換え、抜粋、パス操作などがあるからです。

特に最後のパスエレメントを返す:t、最後のパスエレメント以外を返す:h、拡張子を除外する:r、拡張子を返す:eは重宝します。${${i:h}:t} のようにネストすることができ、これはファイルパスである変数$iの、最後のディレクトリ(つまり、そのファイルの所属するディレクトリの名前)を返します。

その他のZshの良い点

.*

Bashで .* を展開すると ... が含まれてしまうのが悩みどころですが、Zshはこれを含まないのがデフォルトです。

また、そもそも * がドットファイルを含むかどうかは GLOB_DOTS オプションによって制御しますが、 (D) glob qualifierを使うとこれをコントロールすることができます。 (D) でGLOB_DOTSが有効になります。明示的に無効にしたい場合は (^D) が使えます。

パイプのプロセス

Bashではパイプで実行されるコマンドは全て別のプロセスで実行されます。

これは少し問題があります。それは、パイプ先がwhileである場合などに、変数の操作やshiftなどが別プロセスに閉じ込められてしまい、意図したように動作しないということです。

Bash 4.2以降ではlastpipeオプションを有効にすることでパイプの最後は同一プロセス内で実行されるようになりました。

Zshはオリジナルのksh同様、パイプの最後のコマンドは同一プロセスで実行されます。

disown

disownはBashにもある、プロセスをジョブテーブルから削除するコマンドです。

しかし、この使い勝手はZshが勝ります。 まず、ジョブを &| をつけてスタートさせると、最初からジョブテーブルに登録せずに開始します。

また、フォアグラウンドで開始してしまったジョブをdisownしたい場合、一回bgでバックグラウンドジョブにまわしてからdisownするか、もしくはSIGCONTを送る必要があります。 しかし、ZshならAUTO_CONTINUEオプションを有効にすれば、停止中プロセスに対してdisownすると自動的にSIGCONTが送られます。

MULTIOS

ZshではMULTIOSオプションが有効な場合

echo Hello > hell1 > hell2

のように複数のファイルに対してリダイレクトできます。

シェルが提供するパラメータ

Zshは非常に便利な変数がシェルによって提供されます。

一例として、Bashでも$EUIDという変数がシェルによって提供されていますが、これはあくまで読み出し専用です。 しかし、Zshはここに値を書き込むことができ、実際にEUIDが変化します。

PCRE

pcreモジュールを使うとコマンドとして、あるいはconditional expression中で強力な正規表現であるPCREを使うことができます。

なお、conditional expression中ではEXTENDED_GLOB相当のグロブを使うことができ、その表現力はPCREには及ばないまでも、正規表現よりもずっと強力です。

強力なモジュール

ZshはC言語で書かれたモジュールを使うことができます。

標準でもPCREのほかにもTCP、UNIXドメインソケット、FTP、FileStat、select(2)などを駆使することができるモジュールが揃っています。

printコマンドはechoよりも素直な解釈をする、使いやすい出力コマンドです。

特に便利なのが-lオプションで、これは引数を改行区切りで出力します。 複数行に渡る文字列を出力したい場合や、配列/ファイル名生成を明確に分けて表示したい場合に便利です。

read

readコマンドはシェルスクリプトでは必須のコマンドです。

しかしZshのreadはとびきり強力です。

-tオプションはタイムアウト秒数を指定することができ、タイムアウトした場合はexit code 1で終了します。

-kオプションは読み込む文字数を指定します。

-qオプションは1文字だけ読み込み、yesまたはnoを回答させます。 入力がyまたはYであればexit code 0で、それ以外の文字であれば1で、タイムアウトした場合やEOFがきた場合は2で終了します。

変数名に続けて?をつけると、後続の文字列がプロンプト文字列として表示されるため、別途printを使う必要がありません。

エスケープシーケンスつきリテラル

文字列中に改行を入れたい場合、多くのプログラミング言語ではリテラルとして\nを使うことができますが、シェルではできません。 echoで改行するのも-eオプションが必要です。

printコマンドはこうしたエスケープシーケンスを理解しますが、リテラルとして記述したい場合もあるでしょう。

Zshは$''というクォートを使うことで、リテラル中でエスケープシーケンスを展開できます。

ヒアストリング

<<を使ったヒアドキュメントは便利ですが、短いワードを入れたい場合は使いにくく、echoなどをパイプすることが多いでしょう。

Zshでは <<< というヒアストリングを使って、単一ワードを標準入力から入れることができます。

select

select複合コマンドはselect(2)のZshインターフェイスではなく、選択肢から選ばせるものです。 Zshスクリプトにおいて使います。

例えば次のように書いたとします。

select w in a b c d
do
  print $w
done

このスクリプトを実行すると、まず次のように表示されます。

1) a  2) b  3) c  4) d
?# 

ここで3を押してエンターを押すと、 $w の値は c になり、selectはプロンプトからループします。

1から4以外の値を入力すると $w の値は空で、selectはプロンプトからループします。

何も入力せずにエンターを押すと、selectは選択肢を表示するところからループします。

EOF(Ctrl+D)を入力すると$wには何も入らず、selectのループを抜けます。

word … term部分を省略すると、positional paramater(位置パラメータ、$@のこと)が使われます。 プログラムでは使いどころがありませんが、関数にすると呼び出しが楽になります。 パラメータ名($w)も省略すると$REPLYが使われます。

プロンプト出力

print -P によって、プロンプトに設定するような%で始まるエスケープシーケンスを利用可能になります。

特に便利なのが文字小を設定する%F%fと、背景色を設定する%K%kです。

これによってechoのような複雑なASCIIシーケンスを使わなくても端末向けのカラー出力が可能です。 例えば

print -P '%F{red}BLUE%f'

とすると赤い文字でBLUEと出力されます。

数値を使うことでカラーコードでの指定も可能です。256色のカラーコードを確認するには

for i in {000..255}; do print -P "$i %K{$i}     %k"; done

とすると良いでしょう。

SIGZERR

シェルスクリプトでは「これらの実行がひとつでも失敗したらアウト」というケースは割とあるものです。 このような場合に全て||をつけるのは見づらいスクリプトになります。

Zshではexit statusが0以外の場合、SIGZERRという仮想のシグナルが発生したように扱うことができます。 これによって、コマンドa, b, cの3つがひとつでも失敗すれば全体を失敗させたい場合、

(
  TRAPZERR() { exit 1 }
  a
  b
  c
) || failed_to_command_function

のように書くことができます。

設定ファイル

zshには5つの設定ファイルがあり、それぞれ/etc/の下、あるいは$ZDOTDIR(普通はホームディレクトリ)の下にあるドットファイルとして読まれます。

ファイル名 ロード条件
zshenv 常に
zprofile ログインシェルの場合
zshrc インタラクティブシェルの場合
zlogin ログインシェルの場合, zshrcより後
zlogout ログインシェルをログアウトした場合

特に常にロードされるファイルzshenvを使えるのは、Bashにはない大きな利点です。

また、$ZDOTDIRによってユーザー設定ファイルの位置をコントロールできるため、設定ファイルそのものを切り替えて使うことも可能です。

Bashのほうが良い点

IPクライアント

Bashでは /dev/tcp/localhost/10000 のようなファイルをリダイレクト先として指定することでIPクライアントとして使うことができますが、Zshではこれができません。

Zshでもzsh/net/tcpモジュールを使うことでTCP通信はできますが、コマンドラインで使うのにはあまり向いていません。 このモジュールは基本的にはncよりも高度なことをしたい(単にTCPをlistenするだけじゃなく、ちゃんとacceptを取りたいとか)場合に使用するもので、ncよりも簡易なBashが持っているような機能はありません。

なお、この機能はBash固有ではなく、kshに由来します。

関数エクスポート

Bashでは関数をエクスポートすると、関数を環境変数化し、子孫プロセスのBashでその関数を呼び出せるようになっています。

Zshではこのようなことはできません。 特にYadを使う場合などはZshではなくBashを使ったほうが便利なこともあるかもしれません。

しかし、これは必ずしも嘆くようなことではありません。 この機能は潜在的にセキュリティホールとなるからです。

Zshプラグイン

Powerlevel10k

Powerlevel10kはPowerlineとよく似た、Zsh向けのプロンプト拡張です。

驚くべきは、Powerlevel10kそのものがZshで書かれていることです(PowerlineはPythonで書かれています)。

ここまでの動画でもPowerlevel10kのクールなプロンプトを使ってきました。 Powerlevel10kにはVCSに関する機能もあります(Powerlevel10k自体はGitに特化していますが、Powerlevel9kのSubversion及びMercurialの機能を使うこともできます)。

次に示すのはPowerlevel10kのGitプロンプト機能です。 今のディレクトリがGitリポジトリであること、今のブランチがmasterであること、stagedが1、changedが1、untrackedが1であることが示されています。

Powerlevel10kのGit機能

VCS機能だけに限ればgrml-zsh-configのほうが優れていることには注意が必要です。

Zsh Syntax Highlighting

Fishのように、有効なコマンド、無効なコマンド、存在するファイル、存在しないファイルなどコンテキストごとに色分けしてくれます。

Zsh auto suggestions

Fishのように、入力中の値から履歴、ファイルなどを推測して候補として補完してくれます。

使い勝手への影響が大きく、好みが分かれるでしょう。

ZLEコマンドとしてhistory-substring-search-uphistory-substring-search-downが追加されます。

これは入力した文字と部分一致するヒストリを辿ります。

検索でも同じことができますが、一致部分がハイライトされるのと、検索コマンドを省略して使えるのがメリットです。

Zshテクニック

ヒストリ検索

Zshのヒストリ検索は強力で便利です。

Viモードでは、ESCでvicmdに入り、/を押せば検索モードに。 あとは正規表現で探したい文字列を入力して、エンターで検索。nで遡っていくことができます。

Viモードの検索では正規表現が使えるほうか、「先のほうを探していくと履歴を遡ることになる」のが特徴です。

一方、EmacsモードではCtrl+Rでインクリメンタルサーチができます。

Viモードと違ってインクリメンタルサーチが特徴ですが、後方検索で遡るようになっている点がviとは逆になっています。 さらにCtrl+Rを押すことでその先を探すことができます。

注意点として、現在のヒストリよりも新しいヒストリを検索するキーバインドはデフォルトでCtrl+Sであることです。 Ctrl+Sは大抵の端末において出力停止に割り当てられており、有効ではありません。代えて “Ctrl+X S” が使えますから、覚えておくと良いかもしれません。

forとグロブの使いこなし

forは便利ですが、forのためのリストをコマンドによって生成する必要があるのであれば、それはパイプを使ってwhile readにしたほうが良いと感じられるでしょう。

Zshはグロブが強力なので、forの活用の余地が増えます。 例えば次の例ではカレントディレクトリを起点に全ての「シンボリックリンクである」.mdファイルに対して処理を行います。

for i in **/*.md(@)
do
  sed -i 's/a Apple/an Apple/g' $i
done

空ディレクトリだけを削除するのも簡単です。forすら必要ありません。

rmdir **/*(/^F)

バックアップファイルを消すのもfindは必要ありません。

rm -v **/*"~"(.)

さらに修飾子を使えばリネーマーの実装も簡単です。 次の例では全ての.HTM, .HTML, .htm, .htmlx拡張子を持つファイルの拡張子を.htmlに統一します。

for i in **/*.(HTM|HTML|htm|htmlx)
do
  mv -v $i ${i:r}.html
done

普通のシェルのラインエディタはシングルラインであり、継続行に入ってからコマンドを編集するのは困難です。このようなforコマンドを for i in **/*.(HTM|HTML|htm|htmlx); do mv -v $i ${i:r}.html;done のように読みにくい1行に纏めるのは編集の都合もあります。

しかし、Zshはマルチラインのエディタです。セミコロンを使うことなく改行してしまうことで快適にコマンドを入力することができます。 もちろん、Viユーザーであれば、hjklによる移動だけでなく、行末まで削除(D)、行結合(J)、行削除(dd)といったコマンドが活躍しますし、ループの中身をyyPとして似たコマンドを複製してから一部編集するというのも便利な使い方です。

Zshを設定するには

まずは基本仕様を固めるために、良い設定ファイルを導入するのが良いでしょう。

grml-zsh-configをグローバルな設定ファイルを使うのならば、

sudo wget -O /etc/zsh/zshrc https://git.grml.org/f/grml-etc-core/etc/zsh/zshrc

とします。 個人の設定ファイルとして使うならば

sudo wget -O ~/.zshrc https://git.grml.org/f/grml-etc-core/etc/zsh/zshrc

とします。 どちらの場合も個人の設定ファイルはホームディレクトリ直下の .zshrc.local ファイルに書きます(これはgrml-zsh-configの仕様です)。

ディストリビューションによっては標準でホームディレクトリ直下の .zshrc.d ディレクトリ以下のファイルを読み込むようになっているかもしれません。 そうなっておらず、そのようにしたい場合、設定ファイルに

for i in ${ZDOTDIR:-$HOME}/.zshrc.d/*(N)
do
  source $i
done

のように記述します。

これを実行するには BARE_GLOB_QUAL オプションが有効である必要があります。 BARE_GLOB_QUAL が無効で EXTENDED_GLOB が有効なら (N) の代わりに (#qN) と書くことができます。 また、 NULL_GLOB オプションが有効に場合、 (N) そのものが必要ありません。

オプションを有効にしたい場合、setoptを使います。 例えばEXTENDED_GLOBオプションであれば

setopt EXTENDED_GLOB

とすると有効になります。 逆に無効にするにはunsetoptを使います。

unsetopt EXTENDED_GLOB

それ以外にもシェルスクリプトとして、コマンドを記述していきます。(例えばzstyleコマンド) 実行する順序が問題になる場合もありますから、注意してください。

設定の例

私が実際に使用している設定がGitHubで公開されています。 この設定はgrml-zsh-configの利用を前提としていることに注意してください。

解説していきましょう。

まずは ~/.zshrc.localとなるものです。

# -*- mode: zsh; -*-
# vi: set ft=zsh

typeset i

# User's local settings
for i in ${ZDOTDIR:-$HOME}/.zshrc.d/*(.N)
do
  source $i
done

# User's site local settings
for i in ${ZDOTDIR:-$HOME}/.zshrc.local.d/*(.N)
do
  source $i
done

unset i

~/.zshrc.d/*にこのリポジトリにある設定ファイルを配置しています。 ~/.zshrc.local.d/*はエイリアスや関数の設定など、マシンごとに固有のものを書きます。

最初に読まれるファイルでは変数をセットします。 この変数は設定ファイルの中で使われます。 どちらもプラグインファイルのパスです。

# -*- mode: zsh; -*-
# vi: set ft=zsh

_ZSH_CONFIG_PLUGINS=(
  /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
  )
_ZSH_CONFIG_P10K=/usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme
#_ZSH_CONFIG_POWERLINE=/usr/share/powerline/bindings/zsh/powerline.zsh

飛んで60ですが、こちらで先程設定した配列変数に指定されたプラグインを呼んでいきます。 私はSyntax Highlightingだけを使っています。

# -*- mode: zsh; -*-
# vi: set ft=zsh

if (( ${#_ZSH_CONFIG_PLUGINS} > 0 ))
then
  for i in "${_ZSH_CONFIG_PLUGINS[@]}"
  do
    source "$i"
  done  
fi

70はZLEに関する設定です。

設定内容はほとんどがこの記事で紹介したものです。 個人的な好みでViキーバインドにしていることに注意してください。

# -*- mode: zsh; -*-
# vi: set ft=zsh

####### VI mode setting #######
setopt vi
KEYTIMEOUT=1
###############################

# Always use menu.
zstyle ':completion:*' menu yes select

# Menu for full path.
setopt GLOB_COMPLETE

####################################
#    inclimental history search    #
####################################

autoload -U history-search
bindkey "^P" history-beginning-search-backward
bindkey "^N" history-beginning-search-forward
bindkey "\e[5~" history-beginning-search-backward
bindkey "\e[6~" history-beginning-search-forward
bindkey -M viins "\e[5~" history-beginning-search-backward
bindkey -M viins "\e[6~" history-beginning-search-forward
bindkey -M vicmd "\e[5~" history-beginning-search-backward
bindkey -M vicmd "\e[6~" history-beginning-search-forward

80はヒストリに関する設定です。

ヒストリに関するオプションはgrml-zsh-configで既に設定されているので、変数だけをセットしていきます。 ヒストリファイルの名前を変更し、より多くのヒストリを保存するようにします。

# -*- mode: zsh; -*-
# vi: set ft=zsh

####### History Settings #######
HISTFILE=$HOME/.zhistory
HISTSIZE=10000
SAVEHIST=5000000
################################

90はプロンプトに関する拡張設定です。

環境変数 $_ZSH_PROMPTが設定されていた場合、~/.zsh.extend.d/$_ZSH_PROMPTというファイルがあればそのファイルを読み込みます。

そうでない場合は、Powerlevel10kが設定されていて、かつ存在するのであれば、Powerlevel10kを使います。 Powerlevel10kでなく、Powerlineを設定していれば、Powerlineを使います。

Powerlevel10kを使う場合、ZshがMercurialリポジトリ下で起動された場合のみMercurial VCSエンジンを有効にします。 これは、Mercurialエンジンがとても重いことを踏まえて、リポジトリで端末を起動した場合などに限るためです。

# -*- mode: zsh; -*-
# vi: set ft=zsh

if [[ -n $_ZSH_PROMPT && -f ${ZDORDIR:-$HOME}/.zsh.extend.d/$_ZSH_PROMPT ]]
then
  source ${ZDORDIR:-$HOME}/.zsh.extend.d/$_ZSH_PROMPT
else
  if [[ -n $_ZSH_CONFIG_P10K && -f $_ZSH_CONFIG_P10K ]]
  then
    prompt off
    source $_ZSH_CONFIG_P10K
    [[ -f ~/.p10k.zsh ]] && source ~/.p10k.zsh

    # If start shell on Mercurial repository, activate Mercurial plugin.
    if hg status 2> /dev/null > /dev/null
    then
      typeset -g POWERLEVEL9K_VCS_BACKENDS=(hg git)
    fi
  elif [[ -n $_ZSH_CONFIG_POWERLINE && -f $_ZSH_CONFIG_POWERLINE ]]
  then
    prompt off
    source $_ZSH_CONFIG_POWERLINE
  fi
fi

最後はオプションです。 これは個別にコメントをつけていきます。

EXTENDED_GLOBBARE_GLOB_QUALはgrml-zsh-configと重複しますが、重要なのでここで改めて設定しています。

# -*- mode: zsh; -*-
# vi: set ft=zsh

# 拡張グロブを有効にします
setopt EXTENDED_GLOB
# Glob qualifierを有効にします
setopt BARE_GLOB_QUAL
# disownしたときにジョブに自動でSIGCONTを送ります
setopt AUTO_CONTINUE
# バックグラウンドジョブの情報を即時表示します
setopt NOTIFY
# "foo${xx}bar" で $xx が配列のとき、 fooabar foobbar foocbar のように展開します
setopt RC_EXPAND_PARAM
# Ctrl+Dでシェルが閉じないようにします。
setopt IGNORE_EOF
# シェルが閉じたときにバックグラウンドジョブにSIGHUPを送ります。
setopt HUP
# grml-zsh-configはMENU_COMPLETEですが、代わりにAUTO_MENUを使います
setopt AUTO_MENU

# コマンドの代わりにディレクトリを指定するとcdするオプションですが、使いにくいのでオフにします
unsetopt AUTO_CD
# cdがpushd相当になるオプションですが、使い分けたいのでオフにします
unsetopt AUTO_PUSHD
# > で上書きするのを防ぐオプションです。 setopt NO_CLOBBER でも同じです
unsetopt CLOBBER
# 対話的シェルでもコメントを入れられるようにするものです。コピペ時に便利ですが、通常使わないのでオフにします
unsetopt INTERACTIVE_COMMENTS