先日、Macをフォーマットしたため、今まで構築したきた開発環境が消えました。
といっても、バックアップはとってあるので戻すことはできるのですが、せっかく綺麗に全部消えたので、もう一度作り直そうかな、と思ったわけです。
そこで、先日までシェルはZshを使っていたのですが、Fish shellに乗り換えてみました。
Zshでも、特に不便さは感じていなかったのですが、使い始めたころはプラグインを入れたり、カスタマイズしてみたり、ちょっと苦労した記憶があります。
しかし、Fish shellを使ってみて、こんなにも簡単にカスタマイズできるのか、と驚きました。
そこで今回は、Fish shellの使い方と、プラグインなしで使いやすくカスタマイズする方法を紹介したいと思います。
さて、もしもこの記事の通りに設定していただければ、プラグインを使わなくても、こんなに可愛い開発環境を作ることができます。

※この記事は2023/08/10に、新しい情報に更新しています。
Fish shellのインストール
まず、Fish shellをインストールする方法です。
さまざまな方法があると思いますが、今回はHomebrewを使っていこうと思います。
Homebrewをインストールしていない方は、こちらの記事をご覧ください。
Fish shellのインストールには、ターミナルに以下のコマンドを入力します。
$ brew install fish
$ brew list
fish	pcre2
インストールが完了したら、Fishを起動してみましょう。
$ fish
ぶじ、Fishが起動すれば、インストールの完了です。
デフォルトシェルをFishに変更する方法
次に、デフォルトシェルをFishに変更してみましょう。
以下のコマンドをターミナルに入力します。(IntelのMacでは検証できていないので、うまく動かない場合は申し訳ございません)
Intel
~> echo /usr/local/bin/fish | sudo tee -a /etc/shells
~> chsh -s /usr/local/bin/fish
Apple Silicon
~> echo /opt/homebrew/bin/fish | sudo tee -a /etc/shells
~> chsh -s /opt/homebrew/bin/fish
ターミナルを開き直し、Fishが起動すれば成功です。

うまくFishが起動しない場合は、ターミナルの「設定」から、「開くシェル」の項目が「デフォルトのログインシェル」になっているかご確認ください。
Nerd Fontsのインストール
カスタマイズに必要なフォントをインストールします。
今回は、アイコンフォントなどもセットになっているNerd Fontsをインストールします。
GitHubから全体をcloneしてもいいのですが、とても時間がかかるので、今回は使いたいフォントだけをインストールすることにします。
今回はCousineNerdFont-Regularを使っていこうと思います。
インストールには、以下のコマンドを実行します。
~> cd ~/Library/Fonts && curl -fLo "CousineNerdFont-Regular.ttf" https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/Cousine/Regular/CousineNerdFont-Regular.ttf
インストールできたら、ターミナルの設定で、フォントをCousineNerdFont-Regularに変更しておきましょう。
ターミナルの環境設定
ターミナル自体の環境設定を行います。というのも、ターミナル全体の背景色などは、Fishの設定ファイルではなく、ターミナルの環境設定から行う必要があります。
この設定を先にしておくことで、コマンドの文字色などを変えたりするときに、色が分かりやすくなり、便利になります。
ターミナル環境設定の方法はここでは省略しますが、私の場合、このような背景色と文字色、そして先ほどインストールしたフォントに変更しました。

さて、背景色と文字色は私好みになったのですが、プロンプトの表示や、コマンドの色が、あまり好きじゃないので、次はそこを変更していきたいと思います。
ブラウザを使った、Fish shellのカスタマイズ
さて、Fishを使えるようになったら、今度はカスタマイズしていきましょう。
といっても、Fishはそのままでも、コマンドの色も変わり、補完機能もしっかりしています。そのままでも使えるのですが、やっぱり自分に合った設定にしていきたいですね。
Fish shellのカスタマイズはとても簡単で、以下のコマンドを入力することで、ブラウザからカスタマイズすることができます。
~> fish_config
このコマンドを入力すると、ブラウザが開かれます。
そこからFishの設定を行うことができます。
今回は「color」と「prompt」の設定をしていきます。
colorsでは、文字の色を選ぶことができます。

選んだら、「Set Theme」をクリックします。

promptでは、プロンプト(コマンドを入力するところを表す部分)を設定することができます。

また、関数は変数、履歴を確認したり、キーバインドを見たり、コマンドの省略形を設定したりすることができます。
とても簡単にカスタマイズでき便利なのですが、その分自由度は低いです。
やはり自分好みにカスタマイズしたければ、設定ファイルを自分で書き換えるしかありません。
また自分で設定ファイルを書き換えている場合、ここから変更してしまうと、その設定ファイルを消してしまうので、注意が必要です。
Fish Shellの設定ファイルについて
Fish Shellを起動すると、ホームディレクトリの.configに、fishフォルダが作成されます。
この中に、Fish shellの設定ファイルが入っています。
fish/
    |-- completions/
    |-- conf.d/
    |-- config.fish
    |-- fish_variables
    `-- functions/
        `-- fish_prompt.fish
さきほど、ブラウザからの設定を行いました。
このとき、fish/の中に、いくつかのフォルダとファイルが作成されます。
設定は、fish/config.fishに書いていきます。
また、fish/functions/フォルダのファイルで、Fish Shellの関数をオーバーライド(機能を上書きすること)することで、カスタマイズすることもできます。
以下でその詳しい方法を見ていきましょう。
lsで表示されるファイル一覧の色を変更する
ここで、lsコマンドを実行してみましょう。
~> ls
すると、このようにファイル一覧が表示されますが、とても見えにくいです。

そこで、~/.config/fish/config.fishに以下のコマンドを追加します。
~/.config/fish/config.fish
if status is-interactive
    # Commands to run in interactive sessions can go here
    export LSCOLORS=Gxfxcxdxbxegedabagacad
end
「if status is-interactive 〜 end」の内側でも外側でもちゃんと動作したのですが、今回、私は内側に追加しました。
まず、LSCOLORSのデフォルトの値はexfxcxdxbxegedabagacadです。これは2文字でセットになっており、文字色と、背景色を表しています。
そして今回変更したいのは、フォルダ(ディレクトリ)です。
ディレクトリを表すのは先頭の2文字なので、デフォルトの値の、exの部分になります。eは青を、xはデフォルトであることを表すので、青い文字にデフォルトの背景、という意味になります。
なので青い文字が見えにくかったのです。
そこで、一番先頭の文字をGに変えることで、ディレクトリには、太字のシアンを指定しました。

その他の部分はちょっと特別なファイルのことだったりしますので、ここでは変更しませんが、変更したい方は色々試してみてください。
Fish shellで使われる様々な文字色を変更する
つづいて、その他の色も変えてみましょう。
~/.config/fish/conf.d/ディレクトリに、mytheme_colors.fishファイルを作成し、以下のソースを作成してみました。
~/.config/fish/conf.d/mytheme_colors.fish
# 使いたい色を登録しておく
set -l blue             00bcc6
set -l light_blue       8ed0ff
set -l green            00c694
set -l dark_green       287480
set -l red              ff6161
set -l pink             c600c0
set -l light_pink       f8bbf6
set -l orange           c66400
set -l yellow           fff92f
# 白と黒は誰が見ても同じなので、グローバルに登録
set -g white            ffffff
set -g black            000000
# 抽象的な名前でグローバルに登録
set -g color_dark       333333
set -g color_discreet   757575
set -g color_main       $blue
set -g color_main_light $light_blue
set -g color_warning    $red
# git color
set -g color_git_main   $green
set -g color_git_dirty  $yellow
# fish color
set -g fish_color_normal            $white                          # デフォルトの色
set -g fish_color_autosuggestion    $color_discreet                 # コマンドの提案の色
set -g fish_color_cancel            --background=$color_main        # 「^c」の色
set -g fish_color_command           $color_main_light               # コマンドの色
set -g fish_color_comment           $color_discreet                 # コメントの色
set -g fish_color_end               $color_main_light               # ; や & などの色
set -g fish_color_error             $red                            # エラーの色
set -g fish_color_escape            $color_discreet                 # \n や \x70 などのエスケープ文字の色
set -g fish_color_match             --background=$color_main_light  # 検索した文字とマッチした時の背景色
set -g fish_color_operator          $light_pink                     # パラメータ演算子の色
set -g fish_color_param             $light_pink                     # 変数
set -g fish_color_selection         --background=$dark_green        # viモードの選択モードで選択された部分の背景色
set -g fish_color_quote             $light_pink                     # echo ''など
set -g fish_pager_color_selected_background  --background=$color_main        # Tab候補の選択などでの背景色
set -g fish_pager_color_progress             $color_main_light               # Tabキーで表示される補完一覧の、左下に表示される文字の色
set -g fish_pager_color_completion           $white                          # Tabキーで表示される補完一覧の文字色
set -g fish_pager_color_prefix               $color_main_light               # Tabキーで表示される補完一覧の、一致した文字の色
すると、このようになります。

ソースについて
ここで、すこしだけ上記ソースの解説をします。
まず、Fishで変数を使うには、setというコマンドを使います。
例えば、以下のようにすると、blueというローカル変数に00bcc6という値を代入する、という意味になります。
set -l blue 00bcc6
また、以下のように、わざわざ色を二回に分けて代入していて無駄なことをしているな、と思った方もいるかもしれません。
set -l blue             00bcc6
set -g color_main       $blue
私も、いろいろ試してみて、最終的にこの方法にしたのですが、もしこの変数を1つにしてしまうと、このようになります。
set -g color_main 00bcc6
これでは、color_mainには何色が代入されているのか、少しわかりにくいです。
ならば、このようにすればどうでしょうか?
set -g color_main_blue 00bcc6
一見、問題はなさそうです。青だとすぐに分かります。
しかし、メインカラーはやっぱり緑に変えたい、と思ったとします。するとcolor_main_greenといったふうに変数名を変更する必要があります。
すると、color_main_blueという変数を使っていた部分すべてをcolor_main_greenに変更しなくてはなりません。
そんなわけで、変数名は、まず直感的に分かる名前の変数を作り、それを少し抽象的な名前の変数に代入する、というふうな二段階構造がいいように思います。
変数名について
また、中にはcolor_mainという変数名に違和感を覚えた方もいるかもしれません。
普通、main_colorなのでは、と思われるかもしれないです。
これは、私個人で勝手に使っている書き方なのですが、変数名を見たとき、最初にmainと書いてあると、その後の単語を見る必要があるのです。mainと書かれていれば、メインの色かもしれないし、メインのフレーズかもしれません。メインの形かもしれません。mainの後ろにある単語を見て、そこでなんの変数なのかが分かります。
しかし、colorと変数名の先頭に書いておけば、色の変数だということがすぐに分かります。これだけで変数を探すときや、整理するときに、とても楽になります。
正しいか間違っているかは分かりませんが、参考になれば……
プロンプトのカスタマイズ
続いて、プロンプト(コマンドを入力するところを表す部分)の見た目をカスタマイズしていきます。
プロンプトをカスタマイズするには、fish_promptという関数をオーバーライドします。
では、~/.config/fish/functions/フォルダに、fish_prompt.fishファイルを作成し、以下の関数を作ります。
~/.config/fish/functions/fish_prompt.fish
function fish_prompt
end
これでプロンプトが表示されなくなります。

このfish_prompt関数に、自分オリジナルのプロンプトを作成していけばいいのです。
以下で、表示したい情報を順番に追加していきます。
# 区切り文字やアイコン(一目でなにか分かる名前にしておく)
set -l separator_triangle   \ue0b0
set -l icon_cross           \uf00d
# 区切り文字などを、少し抽象的な名前で登録する
set segment_separator       $separator_triangle
set icon_miss               $icon_cross
set color_user
# 文字色と背景色を指定して、文字を表示できる関数
function _segment
    set_color -b $argv[1] $argv[2]
    echo -n "$segment_separator "
end
# 現在のディレクトリとその場所を表示する関数
function _prompt_dir
    printf ' %s ' (prompt_pwd)
    _segment $color_user $color_dark
end
# ユーザ名を表示するための関数
function _prompt_user
    printf '%s ' (set_color $white)(whoami)
    _segment normal $color_user
end
function fish_prompt
    set -l last_status $status
    set_color -b $color_dark $white
    if [ $last_status -gt 0 ]
        echo -n (set_color $white)" $icon_miss "
        set color_user $color_warning
    else
        set color_user $color_main
    end
    _prompt_dir
    _prompt_user
end
まず、11〜15行目で_segmentという関数を作成しています。
これは、プロンプトに表示する文字列やその背景色をかんたんに指定して、文字を表示できる関数です。
次に、17〜21行目で_prompt_dirという関数を作成し、そこに現在のディレクトリを表示する命令を書いています。
現在のディレクトリは、prompt_pwdに入っています。
さらに、23〜27行目で_prompt_userという関数を作成し、そこにユーザ名を表示する命令を書いています。
ユーザ名は、whoamiに入っています。
31行目で使っている$statusは、fish_prompt関数の中で使うことができ、普通は0が入っていますが、ミスをした場合、それに応じた数値が代入されます。
つまり、$statusの値が0よりも大きいときに×マークを表示するようにすれば、エラーのときだけ×マークが表示されるようになります。
実際に表示してみると、このようになります。

Gitに対応させる
つづいて、Gitに対応させてみましょう。
FishはデフォルトでGitに対応しているのですが、プロンプトを初期化してしまったので、git initしても、見た目は変わりません。

やはりブランチ名とかを表示させたいわけです。
そこで、fish_prompt.fishを次のように変更しました。
~/.config/fish/functions/fish_prompt.fish
# 区切り文字やアイコン(一目でなにか分かる名前にしておく)
set -l separator_triangle   \ue0b0
set -l icon_cross           \uf00d
# 区切り文字などを、少し抽象的な名前で登録する
set segment_separator       $separator_triangle
set icon_miss               $icon_cross
set color_user
set color_git_status_bar    #追加
# 文字色と背景色を指定して、文字を表示できる関数
function _segment
    set_color -b $argv[1] $argv[2]
    echo -n "$segment_separator "
end
# 現在のディレクトリとその場所を表示する関数
function _prompt_dir
    printf ' %s ' (prompt_pwd)
    _segment $color_user $color_dark
end
# ユーザ名を表示するための関数
function _prompt_user
    printf '%s ' (set_color $white)(whoami)
    # _segment normal $color_user (削除)
    # 〜ここから〜
    if command git rev-parse --is-inside-work-tree >/dev/null 2>&1
        _change_color_git_status_bar
        _segment $color_git_status_bar $color_user;
    else
        _segment normal $color_user
    end
    # 〜ここまでを追加〜
end
# 〜ここから〜
function _change_color_git_status_bar
    if [ (_is_git_dirty) ]; set color_git_status_bar $color_git_dirty
    else; set color_git_status_bar $color_git_main; end
end
function _is_git_dirty
    echo (command git status -s --ignore-submodules=dirty 2> /dev/null)
end
function _prompt_git
    if command git rev-parse --is-inside-work-tree >/dev/null 2>&1
        set -l git_branch (command git symbolic-ref HEAD 2> /dev/null | sed -e 's|^refs/heads/||')
        _change_color_git_status_bar
        set_color -b $color_git_status_bar
        printf '%s ' (set_color $black)$git_branch
        _segment normal $color_git_status_bar
    end
end
# 〜ここまでを追加〜
function fish_prompt
    set -l last_status $status
    set_color -b $color_dark $white
    if [ $last_status -gt 0 ]
        echo -n (set_color $white)" $icon_miss "
        set color_user $color_warning
    else
        set color_user $color_main
    end
    _prompt_dir
    _prompt_user
    _prompt_git      # 追加
end
これで、Gitで管理しているディレクトリでは、このように表示されるようになります。

アイコンフォントで可愛くする
さて、最低限の機能ができたわけですが、まだなんだか寂しい気がします。
そこで、アイコンフォントを使って可愛くしましょう。
~/.config/fish/functions/fish_prompt.fish
# 区切り文字やアイコン(一目でなにか分かる名前にしておく)
set -l separator_triangle   \ue0b0
set -l icon_cross           \uf00d
set -l icon_plus                    \uf067  # 追加
set -l icon_three_point_reader      \uf4eb  # 追加
set -l icon_octocat                 \uf113  # 追加
# 区切り文字などを、少し抽象的な名前で登録する
set segment_separator       $separator_triangle
set icon_miss               $icon_cross
set icon_untracked          $icon_three_point_reader    # 追加
set icon_git_symbol         $icon_octocat               # 追加
set icon_git_dirty          $icon_plus                  # 追加
set icon_home               \uf4e2  # 追加
set icon_folder             \uf07c  # 追加
set color_user
set color_git_status_bar
# 文字色と背景色を指定して、文字を表示できる関数
function _segment
    set_color -b $argv[1] $argv[2]
    echo -n "$segment_separator "
end
# 現在のディレクトリとその場所を表示する関数
function _prompt_dir
    # 〜ここから〜
    if [ $HOME = $PWD ]; printf ' %s ' $icon_home
    else; printf ' %s ' $icon_folder; end
    # 〜ここまでを追加〜
    printf ' %s ' (prompt_pwd)
    _segment $color_user $color_dark
end
# ユーザ名を表示するための関数
function _prompt_user
    printf '%s ' (set_color $white)(whoami)
    if command git rev-parse --is-inside-work-tree >/dev/null 2>&1
        _change_color_git_status_bar
        _segment $color_git_status_bar $color_user;
    else
        _segment normal $color_user
    end
end
# 〜ここから〜
function _git_prompt_untracked
    echo (command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- :/ 2> /dev/null)
end
function _git_status_symbol
    if [ (_git_prompt_untracked) ]; echo -n "$icon_untracked "
    else if [ (_is_git_dirty) ]; echo -n "$icon_git_dirty "; end
end
# 〜ここまでを追加〜
function _change_color_git_status_bar
    if [ (_is_git_dirty) ]; set color_git_status_bar $color_git_dirty
    else; set color_git_status_bar $color_git_main; end
end
function _is_git_dirty
    echo (command git status -s --ignore-submodules=dirty 2> /dev/null)
end
function _prompt_git
    if command git rev-parse --is-inside-work-tree >/dev/null 2>&1
        set -l git_branch (command git symbolic-ref HEAD 2> /dev/null | sed -e 's|^refs/heads/||')
        set -l git_status_symbol (_git_status_symbol)       # 追加
        _change_color_git_status_bar
        set_color -b $color_git_status_bar
        printf '%s ' (set_color $black)"$icon_git_symbol $git_branch $git_status_symbol"        # 変更
        _segment normal $color_git_status_bar
    end
end
function fish_prompt
    set -l last_status $status
    set_color -b $color_dark $white
    if [ $last_status -gt 0 ]
        echo -n (set_color $white)" $icon_miss "
        set color_user $color_warning
    else
        set color_user $color_main
    end
    _prompt_dir
    _prompt_user
    _prompt_git
end
このようにアイコンが入って可愛くなります。

まとめ
Fish shellはとても使いやすい上に、カスタマイズしやすく、プラグインを使わなくても可愛いデザインにすることができます。
そして、Nerd Fontsを使うと、Powerlineフォント+可愛いアイコンフォントを使うことができ、自由度が増します。
自分だけの環境を作って、開発を楽しみましょう!











