今回は、前回予告した通り、便利シェル「fish」の設定ファイル「config.fish」について説明する。fishでは、構文が「bash」や「zsh」とは異なるので注意が必要だ。

設定ファイルの書き方

fishでは、起動時にホームディレクトリ以下にある「.config/fish/config.fish」ファイルに書いてある内容を実行するので、設定の変更や環境変数のセットといった処理はこのファイルに書いておくとよい。例えば、次のような内容だ。

set -x EDITOR nvim
set -x SVN_EDITOR nvim
set -x PATH ~/Documents/tttcmds/bin $PATH

switch $USER
case root toor
        set --export LANG en_US.UTF-8
case '*'
        set --export LANG ja_JP.UTF-8
end

if not pgrep ssh-agent > /dev/null
        rm -f ~/.ssh-agent.rc
        ssh-agent > ~/.ssh-agent.rc
end

fishでは、変数の設定は「=(イコール)」で代入するのではなく、「set」による指定で行うようになっている。setに指定されているオプション「-x」は、「環境変数として設定する」ということを示すもので、「set -x EDITOR nvim」で、「値を『nvim』として環境変数EDITORを設定する」という意味になる。なお、「set -x」は「set —export」の短縮形であり、「bash」や「zsh」なら「export EDITOR=nvim」と書いているところだ。

また、分岐構文もbashやzshとfishでは異なっている。fishの分岐構文については、上記の設定ファイルを見れば何となくわかってもらえるのではないだろうか。swtich構文では指定された値に対応する処理に分岐し、「if not」はその後に続くコマンドの実行結果が0以外だった場合に一致するといった処理になっている。

インタラクティブシェルとして使う限りは、bashやzshとfishの構文が違っていることにあまり気付かないかも知れないが、制御構文を使うとなれば話は別だ。

こうした違いを念頭に置きつつ、プロンプトをカスタマイズする設定の例をご覧いただこう。

function prompt_pwd
        set -l realhome ~
        awk -v PWD=(echo $PWD | sed "s|^$realhome|~|") -v COLS=$COLUMNS \
        'BEGIN {
                limit=COLS*4/10
                len=length(PWD)
                if (len>limit) {
                        print "..." substr(PWD, len-limit)
                }
                else {
                        print PWD
                }
        }'
end

function fish_prompt
        set -l hostname (hostname -s | tr 'a-z' 'A-Z')
        switch $hostname
        case 'MAC*'
                set hostname MAC
        end
        set -l color_cwd
        set -l color_hst
        set -l suffix
        switch $USER
        case root toor
                set color_cwd $fish_color_cwd_root
                set color_hst $color_cwd
                set suffix '#'
                echo -n -s (set_color $color_hst) "$hostname" ' ' (set_color     $color_cwd) (prompt_pwd) "$suffix " (set_color normal)
        case '*'
                set color_cwd $fish_color_cwd
                set color_hst white
                set suffix '%'
                echo -n -s "$hostname" ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
        end
end

fishでは、「関数を再定義すること」が「設定の変更」になっている。例えば、「fish_prompt」という関数はプロンプトを出力する関数だ。この関数を自分の好きなプロンプトを出力する内容に再定義すれば、表示するプロンプトを変更することができる。上記の例だと、プロンプトにターミナルの幅を考慮して短縮化したカレントパスを表示させたり、rootとそれ以外のユーザーで表示する色やプロンプト記号を変更したりといった処理を行っている。

fishスクリプトを参考に書き換える

fishの場合、それほどゴリゴリと設定しなければならないことはないだろう。だが、もしいろいろ書き換えたいのであれば、インストールされているfishスクリプトを参考に、内容をコピー&ペーストしながら作業すれば良いのではないかと思う。

fishがインストールされていれば、デフォルトの設定ファイルであるconfig.fishがシステムのどこかにデプロイされているはずだ。このファイルをfind(1)コマンドなどを使って探し出し、さらにその周辺にあるfishスクリプトを探していく。例えば、次の例だと/usr/local/share/fish/以下にいろいろなfishスクリプトがあるようだ、ということがわかる。

% find / -type f -name config.fish 2> /dev/null
/usr/local/share/fish/config.fish
/usr/local/etc/fish/config.fish
/Users/daichi/.config/fish/config.fish
% ls /usr/local/share/fish/
__fish_build_paths.fish groff                   vendor_conf.d
completions             man                     vendor_functions.d
config.fish             tools
functions               vendor_completions.d
% ls /usr/local/share/fish/functions/
__fish_append.fish
__fish_bind_test1.fish
__fish_bind_test2.fish
__fish_cancel_commandline.fish
__fish_commandline_test.fish
__fish_complete_abook_formats.fish
__fish_complete_ant_targets.fish
__fish_complete_atool_archive_contents.fish
...略...
la.fish
ll.fish
ls.fish
man.fish
math.fish
N_.fish
nextd-or-forward-word.fish
nextd.fish
open.fish
popd.fish
prevd-or-backward-word.fish
prevd.fish
prompt_hostname.fish
prompt_pwd.fish
psub.fish
pushd.fish
realpath.fish
seq.fish
setenv.fish
string.fish
suspend.fish
trap.fish
type.fish
umask.fish
up-or-search.fish
vared.fish
%

そうして発見したfishスクリプトの中身を、試しに表示させてみる。制御構文は簡単なものだし、使える機能もそれほど難しいものではない。bashやzshの設定ファイルを書けるスキルがあれば、こうしたスクリプトから使えそうなところを切り出し、好きなように書き換えて使えるだろう。

% cat /usr/local/share/fish/functions/ls.fish
#
# Make ls use colors if we are on a system that supports that feature and writing to stdout.
#
if command ls --version >/dev/null ^/dev/null
    # This appears to be GNU ls.
    function ls --description "List contents of directory"
        set -l param --color=auto
        if isatty 1
            set param $param --indicator-style=classify
        end
        command ls $param $argv
    end

    if not set -q LS_COLORS
        if command -s dircolors >/dev/null
            set -l colorfile
            for file in ~/.dir_colors ~/.dircolors /etc/DIR_COLORS
                if test -f $file
                    set colorfile $file
                    break
                end
            end
            # Here we rely on the legacy behavior of `dircolors -c` producing output suitable for
            # csh in order to extract just the data we're interested in.
            set -gx LS_COLORS (dircolors -c $colorfile | string split ' ')[3]
            # The value should always be quoted but be conservative and check first.
            if string match -qr '^([\'"]).*\1$' -- $LS_COLORS
                set LS_COLORS (string match -r '^.(.*).$' $LS_COLORS)[2]
            end
        end
    end
else if command ls -G / >/dev/null ^/dev/null
    # It looks like BSD, OS X and a few more which support colors through the -G switch instead.
    function ls --description "List contents of directory"
        command ls -G $argv
    end
end
%

設定を行うときのポイントは、「簡単にできるようであればサクッと書いてしまい、込み入ってきそうだったらさっさと止める」ことだと思う。凝り始めると設定を変更すること自体が目的になり、本来やろうとしている「仕事を減らす」という点がぼやけてしまう。あくまでも、将来を見据えて「これを設定しておけば、作業が楽になる」と考えられる内容を設定するようにしよう。