fish shellを実質的なデフォルトシェルにするbashrc

by
カテゴリ:

fish shell、便利ですよね。デフォルトでもシンタックスハイライトや補完がよく効いてチューニングしなくてもそこそこ高速に起動するので日常使いしています。これまで chsh -s /usr/bin/fish してデフォルトシェルにfish shellを指定していましたが、Nixやhome-manager経由でfish shellを入れると特殊な場所にインストールされるため、chshできない問題に遭遇しました。

これは、chshに指定可能なシェルが/etc/shellsに記載のものに限られるためです。しかし、nix経由で入れたfish shellのパスは/nix/store/zp86jycn34phfs6mzlf03pji09qp3vqs-fish-4.0.0/bin/fishなどハッシュ値が含まれるため、/etc/shellsへの追記が適しません。

$ cat /etc/shells
# Pathnames of valid login shells.
# See shells(5) for details.

/bin/sh
/bin/bash
/bin/rbash
/usr/bin/sh
/usr/bin/bash
/usr/bin/rbash

/bin/zsh
/usr/bin/zsh
/usr/bin/git-shell
/usr/bin/systemd-home-fallback-shell
/usr/bin/fish
/bin/fish

どうしようと思っていたところにvim-jp slackで、Arch WikiのFishを参考にするといいよと教えてもらいました。ここにはbashをデフォルトシェルとしつつ、bashrc内でexec fishすることにより、bashからfishに移行する方法が紹介されています。 exec fishすると、bashプロセスがfishプロセスに置き換わるため、fishを終了した際にbashに戻ることがなくなります。従って単純にbashrcexec fishするとbashを利用不能になりますが、うまく条件分岐して、ユーザーが明示的にbashを起動することも可能にしています。

bashを経由しても起動時間に大差なさそうなことも確認済みです。その差わずか7ms。もうちょっとbashやfishのオプションは調整してもいいかもですが、まあ差を知るには十分でしょう。

$ hyperfine -w 5 -r 50 -N 'bash -i -c "exec fish -N -c \"exit 0\"; exit 0"'
Benchmark 1: bash -i -c "exec fish -N -c \"exit 0\"; exit 0"
  Time (mean ± σ):      10.3 ms ±   2.6 ms    [User: 5.5 ms, System: 4.5 ms]
  Range (min … max):     8.5 ms …  22.9 ms    50 runs

$ hyperfine -w 5 -r 50 -N 'fish -N -c "exit 0"'
Benchmark 1: fish -N -c "exit 0"
  Time (mean ± σ):       3.6 ms ±   0.9 ms    [User: 2.5 ms, System: 0.9 ms]
  Range (min … max):     3.0 ms …   6.5 ms    50 runs

というわけで、以下を~/.bashrcの先頭に記載しました。

if [[ $- == *i* && $- != *c* && $- != *s* ]] && command -v fish &>/dev/null && [[ $(ps --no-header --pid=$PPID --format=comm) != "fish" ]]; then
  if shopt -q login_shell; then
    exec fish --login
  else
    exec fish
  fi
fi

$-にはbashのオプションが含まれています。これを利用して、以下のケースではfishに移行しないようにしています。

ということになります。

ちなみにArch Wikiではもっとシンプルそうな書き方をしていますが、問題があります。

あとでArchWikiを修正しないとですね!

if [[ $(ps --no-header --pid=$PPID --format=comm) != "fish" && -z ${BASH_EXECUTION_STRING} ]]
then
	shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION=''
	exec fish $LOGIN_OPTION
fi

ENJOY!