Nix学習備忘録Part 2-1: Home ManagerでZ Shellを導入する

2022-01-30T17:18:32Z

1 はじめに

本記事ではHome Managerを用いてZ Shellを導入し、各種設定を行います。 先にhome.nixの記述を載せているので、筆者と同じ設定で取り敢えず済ませたい方は、 2.12.23.1 だけ読めば設定できます。残りは各種設定の解説です。

zshの設定に詳しい方は、Home Managerのzsh.nix を見て、ご自身の.zshrcと見比べながら設定すれば、簡単に移行できると思います。

Z shell(ズィーシェル、zsh)はUnixのコマンドシェルの1つです。

そのため、先に軽くシェルの説明を行います。ご存じの方は読み飛ばして頂いて構いません。

1.1 シェルとは

Unix系のOSを操作するにはカーネル(kernel)と呼ばれる場所に命令を出す必要がありますが、 ユーザーが直接カーネルに命令を出すことはできません。 カーネルの周りを覆う殻、すなわちシェル(shell)を介して命令を出す必要があります。 このシェルを起動し、使用するためのソフトが端末(terminal)です。

シェルとは

シェルにも色々な種類があり、bashやzshの他、fishやeshellなど、数多くあります。 Linuxの場合、標準はほとんどがbashというシェルになっています。 MacOSの場合、10.15 Catalinaからデフォルトのシェルはzshとなっています。

個人的に調べた感じだと、bash以外のシェルだと、zshとfishが人気だと感じました。 本記事では、bashより柔軟で使いやすいと言われており、設定に関する記事も豊富なzshを導入していきます。

2 今回行うHome Managerを用いたzshの設定

「細々した設定の説明は良いから、取り敢えず筆者と同じ設定使ってみて、後で適当に自分で弄るわ!」 って方もいらっしゃると思うので、今回作る完成形を先に載せておきます。

2.1 home.nixの記述

今回行うzshの設定

以下の記述を~/.config/nixpkgs/home.nix内の前回の記事 のひな形に書いたprograms = {};の中に記述します。

    zsh = {
      autocd = true;
      defaultKeymap = "emacs";
      dotDir = ".config/zsh";
      enable = true;
      enableAutosuggestions = true;
      enableCompletion = true;
      enableSyntaxHighlighting = true;
      envExtra = ''
        if [ -e ~/.nix-profile/etc/profile.d/nix.sh ]; then
          . ~/.nix-profile/etc/profile.d/nix.sh
        fi
      '';
      initExtra =''
        setopt no_beep
        setopt auto_pushd
        setopt pushd_ignore_dups
        setopt inc_append_history
      '';
      history = {
        extended = true;
        ignoreDups = true;
        save = 1000000;
        share = true;
        size = 1000000;
      };
      oh-my-zsh = {
        enable = true; 
        plugins = [
          "extract"
          "fzf"
          "git"
          "web-search"
        ];
      };
      shellAliases =
        {
          h = "history";
          hs = "history | grep";
          hsi = "history | grep -i";
          ll = "ls -alF";
          la = "ls -A";
          l  = "ls -CF";
          vi = "nvim";
          view = "nvim -R";
          vim = "nvim";
        };
      zplug = {
        enable = true;
        plugins = [
          { name = "romkatv/powerlevel10k"; tags = [ as:theme depth:1 ]; }
        ];
      };
    };

2.2 既定のシェルをzshへ変更する

home-manager switchで上のzshの設定が反映されたhome.nixを読み込みます。 しかし、これだけでは再びターミナルを開き直しても、いつも通りのシェルが起動します。 現在のシェルを変更するために、chshコマンドを使って設定します。

$ echo $SHELL
/bin/bash
$ command -v zsh
/home/username/.nix-profile/bin/zsh
$ sudo chsh -s "$(command -v zsh)" "${USER}"

これでターミナルを起動し直すと(なぜか私は2回起動し直す必要がありました)、 後述しますが、初回起動時のみ、powerlevel10k というシェルの見た目を簡単に設定できるものが起動します。

質問に答えていくだけですが、わからない場合は次で紹介するpowerlevel10kのパートで設定方法を確認してください。

Visual Studio Codeでの既定のシェルの設定
Ctrl+Shift+@でターミナルを起動し、+ボタンの右にあるをクリックし、 既定のプロファイルの選択をクリックして、zshを選択する。

3 zshのカスタマイズ

前節で設定したものについて細かく見ていきます。 本来なら基本設定から解説した方が良いですが、上の設定をコピペでスタートした人のために、まずpowerlevel10kから解説します。

3.1 powerlevel10k: ターミナルの見た目を簡単に設定する

GitHubのリポジトリはこちら(https://github.com/romkatv/powerlevel10k)

3.1.1 home.nixへの記述

home.nixprograms.zsh内の以下の部分で導入しています:

      zplug = {
        enable = true;
        plugins = [
          { name = "romkatv/powerlevel10k"; tags = [ as:theme depth:1 ]; }
        ];
      };

ちなみに、Home Managerを使わずにやると、手動で設定すること(ZSH_THEMEの設定等)が少しだけ多いのですが、 その辺りの細々した作業も全部やってくれます。有難い。。。

3.1.2 フォントの設定

ただし、このフォント設定を行わないと、アイコンが上手く表示されなかったりします。 別に気にしない方や、ターミナルのフォントを変更したくない場合はこちらのステップは省略可能なので、次のステップに進んでください。

  1. powerlevel10kのGitHubリポジトリにアクセスする。
  2. README.mdの1/4くらいにあるFontsを見つける(「Fonts」でページ内検索するなどして見つけてください)。
  3. Manual font installationにある、以下の4つのフォント(ttfファイル)をダウンロードする:
  • MesloLGS NF Regular.ttf
  • MesloLGS NF Bold.ttf
  • MesloLGS NF Italic.ttf
  • MesloLGS NF Bold Italic.ttf
  1. 各ファイルを開くと、「インストール(Install)」または「フォントをインストール(Install Font)」というボタンがあるので、クリックしてインストールする。

以下、ターミナルごとにフォントの設定を行う:

3.1.3 powershell10kの設定について

powershell10kを設定した後、初めてシェルを起動すると、設定が始まります。 基本的には、質問に対して答えていくだけですが、一応英語が苦手な方のためにも質問の内容を記載します。 ちなみに、次の節で解説するように、設定を変えたい場合はいつでも簡単に再設定できるので、忙しい方は適当に答えてても問題ありません。

最初の4つの質問は、アイコンフォントが文字化けするかどうかの質問です。 前のフォントの設定を飛ばしている場合、いくつかnで答える箇所が出るかもしれません。

  1. ---><---の間にある文字がダイヤモンド(◆)に見えているか?という質問です。 ダイヤモンドが表示されていればy、文字化けしていればnを入力してください。
  2. 同様に、次は南京錠()が表示されているか?という質問です。 南京錠が表示されていればy、文字化けしていればnを入力してください。
  3. 次はDebianのロゴである渦巻き模様が見えているかという質問です。 渦巻き模様が表示されていればy、文字化けしていればnを入力してください。
  4. 次はXを間に挟んで、様々な色の様々なアイコンが表示されているかという質問です。 恐らく、X(赤色でgitの文字)X(緑でGitHubのアイコン)X(黄色で時計)X(青でダイヤモンド)X(紫で家アイコン)X(水色でファイルアイコン)X(赤色で白抜きファイルアイコン)X(緑でよくわからないもの)Xが表示されると思います(違うかもしれません)。 文字化けしていなければy、していればnを入力してください。

ここから先は好みでプロンプトの見た目を設定していきます。 どのプロンプトを選ぶかによって、質問の量や内容は変わりますが、一応自分の設定の分だけ載せておきます。 一応選定理由も載せていますが、個人の好みなので、好きなように設定してください。

  1. プロンプトの見た目です。情報がごちゃごちゃし過ぎるのはあまり好きではないので、私はLeanにします。1を選択します。
  2. 文字セットの設定です。Unicodeが良いので、1を選択します。
  3. プロンプトに使う色の数です。256色で1を選択します。
  4. プロンプトの右側に表示する現在時刻の表示設定です。時刻くらいなら表示してても良いかなと思うので、24-hours format2を選択します。
  5. 入力行を改行するか否かの設定です。カレントディレクトリへのパスが長くなると、かなり窮屈になってしまうので、Two Lines2を選択します。
  6. ディレクトリ情報と時刻情報の間にラインを引くか否かの設定です。シンプルにDisconnected1を選択します。
  7. 改行に付けるフレームの設定です。ここもシンプルにNo frame1を選択します。
  8. コマンドの実行後に空行を入れるかどうかの設定です。前に実行したコマンドができるだけ多く表示されていた方が好きなので、Compact1を選択します。
  9. アイコンを表示するかどうかの設定です。直感的にわかりやすいと思うので、Many icons2を選択します。
  10. 表示により詳しい説明を加えるかどうかの設定です。流石に長くなり過ぎるので、Concise1を選択します。
  11. コマンド実行後に、以前のコマンドの部分にもディレクトリ情報等を残すか否かの設定です。よく考えれば要らないなと思ったので、Nonを選択します。
  12. インスタントプロンプトに関する設定と出ていますが、よくわからないので、推奨されているVerboseの1を選択します。
  13. 設定を完了して保存するかの質問です。Yesyを選択します。

これで設定が完了しました!個人的にプロンプトの見た目が自分好みだと、モチベーションにも繋がります。

3.1.4 powerlevel10kの設定をやり直す

ここまで行った設定はいつでも設定し直すことができます。 やり方はコマンド一つです。p10k configureを実行すると、再度設定画面が開き、再設定できます。 飽きたら別の見た目にいつでもカスタマイズできるということですね。

3.2 zshの便利なコマンド

色々ありますが、便利だなと思った2つだけピックアップします。 自分自身入門したてなので、他にも便利なショートカットがあれば教えて頂けると幸いです。

  • Ctrl+k: カーソル位置から行末まで削除
  • Ctrl+r: 以前実行したコマンドの検索

3.3 基本設定

Home Managerで準備されている設定と、されていない設定があります。 準備されていない設定に関してはzshinitExtraで設定されています。

まず、

  • enableAutosuggestions = true;でサジェストを有効化、
  • enableCompletion = true;で補完の有効化、
  • enableSyntaxHighlighting = true;でシンタックスハイライトの有効化

を行っています。

envExtraに記述している内容はNixを使う上で必要な設定なので、 あまり気にしなくてOKです。

3.3.1 auto_cd: ディレクトリ名の入力だけでそのディレクトリに移動する

zsh内の以下の行で設定しています(通常は.zshrcsetopt auto_cdと記述します):

      autocd = true;

これを設定しておくと、cdと入力しなくても、ディレクトリの名前だけで移動できます。

3.3.2 no_beep: ビープ音を消す

initExtra内の以下の行で設定しています。

        setopt no_beep

できない操作を試みたときにビープ音を鳴らさないようにする設定です。 私はいちいちうるさいと感じるので、設定しています。

3.3.3 auto_pushdとpushd_ignore_dups: ディレクトリ移動をスタックで管理する

次の2つは少し難しいですが、pushdpopdというディレクトリの移動に便利な操作に関する設定です。 先にpushdpopdについて説明します。 スタック(stack)でディレクトリの移動を管理できるというものです。

スタック(stack)って何?
データ構造の一つです。pushで最後に入れた(last-in)ものを、 popで頭から取り出す(first-out)ことができるLIFO(last-in first-out)のデータ構造です。 よく挙げられる例が、本を机の上に積み上げる例です。1冊1冊本を積み上げたとき、 最初に取り出せるもの(first-out)は一番上にある本ですが、 これは最後に積んだ本(last-out)ですね。 本を積む操作がpush、本を取り出す操作がpopと考えて頂ければOKです。

pushdpopdの「d」はディレクトリ(directory)の頭文字です。

pushdpopdのデモ

以下、コマンドの状況の説明を#以降で説明しています。 自分で動作確認をする場合は、#以降は入力せずに行ってください。

$ cd ~                       # ホームディレクトリに移動
$ mkdir tmp                  # デモ用のディレクトリ作成
$ mkdir tmp/hoge             # デモ用のディレクトリ作成
$ mkdir tmp/hoge/fuga        # デモ用のディレクトリ作成
$ pwd                        # 現在位置の確認
/home/username
$ pushd tmp/hoge             # スタックにtmp/hogeを追加
~/tmp/hoge ~                 # 最後に移動した~/tmp/hogeがスタックの先頭に追加されている
$ pwd                        # 現在位置の確認
/home/username/tmp/hoge      # 移動できている
$ pushd fuga                 # スタックにfugaを追加
~/tmp/hoge/fuga ~/tmp/hoge ~ # ~/tmp/hoge/fugaがスタックの先頭に追加されている
$ pwd
/home/username/tmp/hoge/fuga # 移動できている
$ popd
~/tmp/hoge ~                 # ~/tmp/hoge/fugaがスタックから取り出された
$ pwd
/home/username/tmp/hoge      # ~/tmp/hogeに移動している
$ popd
~                            # ~/tmp/hogeがスタックから取り出された
$ pwd
/home/username               # ~に移動している
$ rm -r tmp                  # 掃除

このような形で、pushdで移動の履歴を残しながら、 戻りたいときにはpopdで戻ることができます。 ちょっと別のディレクトリに用事があるけど、すぐ戻りたいとき等に便利そうですね。

さて、前置きが長くなりましたが、setopt auto_pushdと記述しておくと、 普段のcdコマンドがpushdとして実行され、普段通りcdで移動した場合でも、 popdで辿って来た道のりを戻ることができます。

これはinitExtra内の以下の行で設定しています:

        setopt auto_pushd

次に、setopt pushd_ignore_dupsですが、これを設定していない状態で、 例えばcd ~を連続して何度も行うと、スタックにずっと~が溜まり続け、 popdをやってもやってもホームディレクトリから動けないという状態に陥ってしまいます。 これを避けるために、重複したディレクトリはスタックに追加しないというのが、 このsetopt pushd_ignore_dupsの設定です。

これはinitExtra内の以下の行で設定しています:

        setopt pushd_ignore_dups

3.3.4 historyを使いやすくする

historyというコマンドで、以前実行したコマンドをチェックすることができ、 !に履歴の番号を添えて実行することで、そのコマンドを再度実行することができます。 このhistoryの機能を使いやすくする設定を行います。

home.nixzsh内にある以下の記述で設定しています:

      history = {
        extended = true;
        ignoreDups = true;
        save = 1000000;
        share = true;
        size = 1000000;
      };

extended = true;は、historyファイルにタイムスタンプを保存するという設定です (通常は.zshrcsetopt EXTENDED_HISTORYと記述します)。

ignoreDups = true;は、historyコマンドで重複するコマンドは保存しないというものです (通常は.zshrcsetopt hist_ignore_dupsと記述します)。 例えば、癖でlsコマンドを何回も実行したりしてしまうことがよくありますが、 そういったものでコマンド履歴を汚さなくてすむようになります。

share = true;は、新しく開いたzshにも履歴をシェアするという設定です (通常は.zshrcsetopt share_histroyと記述します)。

save = 10000000;は、履歴ファイルに保存する履歴の件数の設定です (通常は.zshrcexport SAVEHIST=1000000と記述します)。

size = 10000000;は、メモリに保存する履歴の件数の設定です (通常は.zshrcexport HISTSIZE=1000000と記述します)。

また、通常historyにコマンドの履歴が保存されるのは、 再度コマンドが入力可能になるタイミングですが、 この設定だと、サーバーにアクセスするような、 しばらく結果がかえって来ないようなコマンドを実行して、 そのままターミナルを閉じてしまった場合などに、履歴にコマンドが残らなくなります。 この保存のタイミングを、実行したとき即座に行うようにする設定が、initExtraにある以下の記述です:

        setopt inc_append_history

3.4 oh my zshによるプラグインの設定

zsh内の以下の行で有効にしています:

      oh-my-zsh = {
        enable = true; 

以下、plugins = [];の中身に欲しいoh my zshのプラグインを書いていくだけです。

3.4.1 extract: ファイルを展開する

oh-my-zshplugins内の以下の行で設定しています:

          "extract"

圧縮ファイルの種類はたくさんありますが、このextractを入れておくと、 extract <ファイル名>で大抵のファイルを展開することができるようになります。 展開できるファイルはoh-my-zshのextractプラグインで見ることができます。

3.4.2 fzf: ファイルを高速に検索する

oh-my-zshplugins内の以下の行で設定しています:

          "fzf"

これにより、ターミナル上でCtrl+tというショートカットでFuzzy Finderというファイル検索が起動し、 ファイル名やディレクトリ名を入れてそこに移動することが可能になります。

3.4.3 git: gitのaliasとfunctionを利用する

oh-my-zshplugins内の以下の行で設定しています:

          "git"

正直、oh-my-zshの設定でデフォルトで入るものなので入れてるというだけです。 aliasについては、oh-my-zshのgitプラグイン を参照すれば良いかと思います。gagit addgbgit branchなど、 たくさんaliasが設定されています。使わないなら削除しても良いかと思います。

3.4.4 web-search: コマンドラインから検索を行う

oh-my-zshplugins内の以下の行で設定しています:

          "web-search"

これにより、コマンドラインからgoogleというコマンドでGoogle Chromeを立ち上げて検索することができるようになります。 何が嬉しいかというと、ターミナルでプログラムを実行したときに出たエラーメッセージを、 そのままターミナル内でコピーして、 例えばgoogle python command not foundなどと入力するだけで、 「python command not found」に関するエラーをGoogle検索することができます。 わざわざブラウザを立ち上げて検索する必要がなくなります。

3.5 外部プラグインの利用について(別記事に回します)

デフォルトで入っていないプラグインでもGitHubから引っ張ってくることを記述できますが、 少し込み入った説明と、プラグインによってはzshの外部に記述をする必要が出てくるので、 今回は説明しないことにします。

4 まとめ

Home Managerを用いてzshを使えるようにしました。 外部プラグインの設定方法については、またいつか記事にまとめます。 外部プラグインの設定についても、ほんの少し複雑に感じるところはあるかもしれませんが、 Nixの強味がかなり活かせ、一度設定してしまえば移行が楽になります。

NixもHome Managerも初心者なので、 理解していないことやわからないことが多くあるので、 アドバイスや、説明に誤り等を発見しましたら、ご指摘願います。

Home Manager、いじればいじるほど凄いなぁとなってる今日この頃です。