最近弊社でも大学生のエンジニアインターンが増えてきて,彼らの成長のために,開発にあたっての知見を効率的に共有する手段を色々と模索している.こういった知見は再利用性も高く,社内に閉じている必要もまったくないので,こうしてブログに書き溜めていくことにしてみようかと思う.第一弾として,端末エミュレータ上でのオペレーションの効率化について記す.
ソフトウェアの開発をやっていると,なんだかんだで端末エミュレータというインタフェースの上で,シェルを使ってオペレーションを行うことが多い.開発に用いるマシンのOSはたいていmacOSかLinuxの何かしらのディストリビューション(Ubuntuが多いか?)だし,プロダクション環境での運用はLinuxを前提することが多いだろう.また,Dockerの普及により,Linuxへの理解の重要性はますます高まっている.こういったUnix-like OSの上で効率的なオペレーションを行う上で,コマンドライン・インタフェースを介したオペレーションへの習熟は避けられない.
とはいえ,CLI上でのオペレーションの学習曲線はかなり急峻で,慣れるまでに時間が掛かるし,慣れていないうちから使いづらいインタフェース上で長時間作業をさせられるのは苦でしかない.そこでこの記事は,(主にZshに関する)様々なtipsを紹介することで,環境構築の手助けをし,開発効率向上の一助となることを期待して書かれている.
以下では,macOS上でのオペレーションを念頭に解説する.Linuxを使っている人は,好きで使っていると思うので,ある程度自分でなんとかしてほしい.
フォントについて#
コマンドやプログラムは1文字違うと動かない.そのため,読み間違いをしないよう,視認性の高い大きなフォントと背景色を心掛ける.MacのTerminal.appは,デフォルトでは背景色が白になっていて,随分と文字が小さい(11 pt.)が,あれは目が痛くなるし読みにくいのでやめた方がいいと思う.たぶん背景は黒の方がいい.フォントは,筆者はMenloの15 pt.に設定しているが,もしかするともうちょっと見やすいフォントがあるかもしれない.最近@hiroqnはMonoidを導入していた.名前もいい.
2020/5/10追記: 最近はJetBrains Mono 20 pt.をデフォルトにしている.
フォントは,"Preferences" (Cmd + ,)の"Profile"の中にある"Font"セクションの"Change..."から変更できる.
初期の設定だと,14 pt.の次の大きなサイズが18 pt.になっていて細かい調整が効かないが,以下のようにして任意のフォントサイズを追加できる.各々のディスプレイなどの環境や視力に合わせて変えてほしい.


"Fixed List"のチェックを外すのを忘れぬよう.
ターミナルでよく見る色は,{white, black, red, green, blue, magenta, yellow, cyan} × {normal, bright} の16色.これらの色を好みに(視認性を担保しつつ!)カスタマイズするのもいいだろう.
ログインシェルについて#
(たぶん「ログインシェル」という用語で大丈夫だと思うのだけど,UserShellという表記も見るのであまり自信がない.間違ってたら優しく教えてください……)
端末を開いて,初めに立ち上がるシェルは,何も設定していなければ多分bash(/bin/bash)になっていると思うが,(個人的な意見としては,)特に強い理由がなければZshに変更した方がいいと思う.Zshは言うなればbashの上位互換みたいなもので,具体的な優位性としては,
- Tabキーによる補完が優秀
cd /u/l/bと入力した状態でTabキーを押すとcd /usr/local/binと展開される- approximate補完を使うと多少のtypoを許容しつつよしなに補完してくれる
- 補完の際に,補完対象のカテゴリ(ファイル,Gitのリビジョン,PIDなど)のカテゴリ分けがキレイに表示されて便利
scpとかでリモートのサーヴァにあるファイルを手元に持ってこようとするときにも,リモートのパスを補完してくれる(!)
- ファイルを一括でrenameしたいときに
zmvが便利すぎるnoglob zmv -W hoge/*.ext ./hoge-*.extみたいなことができてすごい
- とにかく設定項目が多くて使いこなすのが無理
と,いろいろすごい.特に補完が強力なのは大事で,どうせ人間はタイプミスをするし,一言一句間違えずにキーボードを正確に打つことは意外と神経を使うので,とにかくTabキーを連打しまくって楽をするように心掛けよう.本質的じゃない部分で消耗するのは本質的でない(本質的に).
ログインシェルを変更するには,chshコマンドを使う.
$ chsh -s $(which zsh)
$(command)というのはcommand substitutionといって,中に書いたコマンドの標準出力への出力内容を,コマンドの引数に渡す文字列として置き換える,という機能である*1.chshでログインシェルを変更したい場合,そのシェルのパスが/etc/shellsに書かれている必要がある./bin/zshとかなら最初から記載されていると思うが,自分で他のシェルをインストールしてログインシェルに設定したい場合などには,追記するのを忘れないこと.
インタラクティブシェルとして使いたい場合のZshの設定ファイルは,だいたい~/.zshrcに書くことになっている.初回起動時に,ウィザードに従うと最低限の.zshrcを生成してくれるはずだ.
補完機能を有効にするには,以下のように書く.
autoload -Uz compinit; compinit
また,approximate completionを有効化するには,以下のように書けばよい.
zstyle ':completion:*' completer _complete _approximate
他にも便利なcompleterがあるが,詳細は20.4 Control Functionsを参照のこと.
あと,補完の際に大文字小文字を区別しないようにしておいた方が精神衛生上よい.
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
プロンプトについて#
プロンプトとは,コマンドを打つときに左に出ているアレのこと.プロンプトに何を表示しておくべきかという議論はいろいろありそうだが,個人的には以下の要件を満たしていれば十分かなと思っている.
- カレントディレクトリが見られる
- VCS(Gitなどのヴァージョン管理システム)の情報が見られる
- 視認性が高い
- 直前のコマンドの終了ステータスがわかる
ユーザ名やらホスト名が必須かというと,ローカルの端末で一人で開発する際にはそんなに重要じゃないのでは……??と思っているが,まぁ普通は表示すると思う.
2020/5/10追記: ユーザ名・ホスト名を表示するのをやめて$だけにした.
カレントディレクトリが見られる#
カレントディレクトリはさすがに常に表示しておいた方がいい.コマンドを実行する際に,現在いるディレクトリ-awareであるべき場面は多いし,いちいちpwdを叩きたくない.
VCS(Gitなどのヴァージョン管理システム)の情報が見られる#
VCS(と言ってもGitしか使わないのだが)の情報が常に見られると便利,というか,今いるブランチを確認するためにわざわざgit branchを叩きたくなさすぎる.Zshはそういった用途のためにvcs_infoというものを提供している.
視認性が高い#
「視認性が高い」という部分は,もしかすると人によって好みがあると思うが,筆者の場合はプロンプトに2行,上に1行の空行を入れて広めにスペースを取り,カレントディレクトリ,vcs_info,ユーザ名・ホスト名ではそれぞれ色を分けている.典型的なシェルは,標準出力・標準エラー出力の直下にプロンプトが表示されるが,あれはあまりに見にくいと思う.一度のコマンド実行あたりに取るスペースが広いので,一画面に収まる情報量は少なくなってしまうが,上の方の出力結果が見たい場合は,スクロールすればいいと信じている.
直前のコマンドの終了ステータスがわかる#
コマンドを実行して,成功したつもりでいたら実は失敗していた,という経験があるかもしれない.エンジニアたるもの出力されたメッセージを見ずに成功と信じるのはご法度だが,標準出力と標準エラー出力が両方端末に接続されていると,一見見分けがつかないので仕方ないといえば仕方ない.そこで,Unixの終了ステータスをうまく活用する.直前の終了ステータスが0でなかった場合,つまり正常終了しなかった場合に,プロンプトの色を変えてやると,正常と異常がひと目で見分けられる.
設定例#
以上を勘案した上で,筆者のプロンプトは以下のようになっている.
autoload -Uz vcs_info
zstyle ':vcs_info:*' formats '(%s)-[%b]'
zstyle ':vcs_info:*' actionformats '(%s)-[%b|%a]'
function precmd() {
psvar=()
LANG=en_US.UTF-8 vcs_info
[[ -n "$vcs_info_msg_0_" ]] && psvar[1]="$vcs_info_msg_0_"
}
PROMPT="
%F{cyan}[%~]%f %1(v|%F{green}%1v%f|)
%(?.%F{yellow}%}.%F{magenta})%n@%m%f $ "
%から始まる文字列は色々に展開されるのだが,詳しくは13 Prompt Expansionを参照してほしい.
キーバインドについて#
Vimmerの筆者でも,単行編集の際にはさすがにEmacsキーバインドに優位性があると思っているので,以下のように設定している.
bindkey -e
これでEmacsキーバインドが有効になる.主要なキーバインドを以下に挙げる.macOSの欄に丸が付いているものは,Cocoa Text Systemでもサポートされているので,普段Macを使っていて普通に接するインタフェースでも利用できる.(事実この記事はこれらのキーバインドを駆使して書かれている!)
| キー | 挙動 | macOS |
|---|---|---|
C-a |
行頭に移動 | o |
C-b |
1文字左に移動 | o |
C-d |
delete | o |
C-e |
行末に移動 | o |
C-f |
1文字右に移動 | o |
C-h |
backspace | o |
C-k |
行末まで削除 | o |
C-n |
1つ次のコマンド | o |
C-p |
1つ前のコマンド | o |
C-u |
1行削除 | |
C-w |
1単語削除 | |
M-b |
1単語右に移動 | |
M-d |
単語を削除 | |
M-f |
1単語左に移動 |
C-yでヤンク(ペースト)できたりとか,他にもある.
metaキーってどうやって打つんだと思った場合,最寄りのEmacsユーザに尋ねてほしい."Preferences..." (Cmd + ,) -> "Profiles" -> "Keyboard"の中にUse Option as Meta keyというものがあるので,筆者はこれを有効化している.
履歴検索#
エンジニアの三大美徳に「怠惰」というものがあるが,面倒な作業を面倒だと思わずに愚直に行ってしまうのは確かによくない.一度打ったコマンドを検索するのに,↑キーを連打しまくったりしていないだろうか.C-rを押すと,今までに実行したコマンドの履歴からインクリメンタルに検索できる.また,M-.を押すと,直前のコマンドの最後の引数が入力される.
Zshの機能の中だと,history-search-endがおすすめ.Zshはデフォルトでも,M-n及びM-pで,現在の入力内容に基づく履歴検索の機能を提供しているが,以下の設定をしておくと,例えばgit reまで打ってからM-pを(何度か)打つと,履歴からgit reから始まるもののみ(git reset ...やgit rebase ...など)を表示してくれ,かつカーソルを行末に移動してくれる.行き過ぎた場合には,M-nで戻れば良い.
autoload -U history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[p" history-beginning-search-backward-end
bindkey "^[n" history-beginning-search-forward-end
筆者はC-rで開始できるreverse-i-searchの挙動が好みでないので,pecoを使って,履歴をいい感じに検索できるようにしている.これに関しては,"peco zsh history"あたりで検索すると,いくらでも情報が出てくるだろう.
2020/5/10追記: 最近はpecoではなくfzfを使っている.
その他#
auto_pushd#
ディレクトリ構造の深海に潜っているとき,間違えて引数無しでcdを実行してしまい,ホームディレクトリまで一気に浮上してしまって戻れない,という経験はおありだろうか.auto_pushdはそんなつらい問題を解決してくれる.
setopt auto_pushd
pushdを実行すると,現在のディレクトリをスタックに積み,popdを実行すると,スタックからpopしてきて,そのディレクトリに移動する.auto_pushdは読んで字の如く,cd時にpushdを自動的に行ってくれる素敵なオプションである.間違ってホームディレクトリに移動してしまっても,popd一発で元の場所に戻れる.
筆者は面倒なのでalias p='popd'と書いているけど,これはやり過ぎかもしれない.
cd時にls#
cdしたらだいたいlsするので,自動で行うようにしている.このせいで画面の内容が流れていくのが余計に速くなっているが,見たければスクロールすればいいと割り切っている.
function chpwd() {
if [ `ls -Al | wc -l` -eq 0 ]; then
echo "\n\nempty directory";
else
echo "\n"
ls
fi
}
zsh-syntax-highlighting#
zsh-syntax-highlightingを入れると,コマンドを入力している途中でもハイライトを行ってくれるようになる.これを入れると,今から実行しようとしているコマンドがそもそも$PATHにあるのかないのかが,実行する前に以前に色でわかるので,typoに対するフィードバックが非常に速い.
macをお使いの場合は,Homebrewを使ってインストールできる.設定手順がインストール後に表示されるので,参照されたい.
$ brew install zsh-syntax-highlighting
終わりに#
当然ながら,環境構築ばかりやっていても成長はしない.しかし,普段の開発の効率を向上させるために,ある程度質の良い環境を用意しておくことも大事ではあるし,少なくとも好んで劣悪な環境の上に立って仕事をすることはないだろう.
こういった環境の整備は,往々にして一度手を付けるとやめられなくなってしまうことが多い.個人的にも,何度.zshrcや.vimrcを編集して朝を迎えたかわからない.読者諸兄においては十分に注意されたい.
脚注#
*1: バッククォートで囲んでも同じ挙動をするのだが,この記事をMarkdownで書いている関係上,表記が大変なのでこちらを使う.
