tl;dr
fishシェルで擬似的にecho {1..9}
のような範囲展開を実現するプラグインを作成しました.
動機
fishシェル便利ですよね! 素の設定でかなり快適に使うことができます.
しかし,他のシェルと比べて不便だなと思う点もあります.
どうやら,fish
のブレース展開は{1..9}
や{a..c}
のような範囲展開ができないようです.
fish
で範囲展開に相当することを行うにはコマンド置換を使います.
# {1..9}相当
touch test(seq 9).txt
# {1..9..2}相当
echo (seq 1 2 9).txt
# {a..c}相当
echo (jot -c 3 a c)
このように一応可能ではある...ものの,タイプ量も増えるし,これまで通り{1..9}
の書式で範囲展開できないかな...と考えていました.
解決
fish
のcommandline
コマンドについて調べていた際にアイデアを思いついたので実装してみました.擬似的にですがfish
で範囲展開が可能になります.
fish
が{1..9}
といった書式を受け付けてくれない以上,入力したコマンドが実行される前に,その内容を書き換えるしかないという発想です.
実装を要約すると以下の通りです.コマンドの実行を行う改行キー(\n
, \r
)にキーバインドして,コマンドラインの内容を書き換えます1.
bind \n 'type -q __expand_range; and __expand_range; commandline -f execute'
bind \r 'type -q __expand_range; and __expand_range; commandline -f execute'
function __expand_range
set -l buf
for line in (commandline) # コマンドラインが複数行だった場合のためのループ
set buf $buf (expand_range $line) # これが処理の本体
end
commandline (string join \n $buf) # 複数行をつなげてコマンドラインに書き出す.
end
ちなみに範囲展開そのもの(expand_range
関数)の実装についてですが,bash
を呼んで展開してもらっています.
bash
を呼んで展開してもらっています.
つまり実体はbash -c 'echo {1..9}'
です.bash
便利.
まとめ
fish
シェルはbash
やzsh
のような多機能性と引き換えにシンプルな姿を得たシェルですが,bash
の機能をつまみ食いすることで,もっと便利に使えるようになりました.
余談
改行にキーバインドするという荒っぽい方法ですが,いろいろ可能性はありそうです.
というのも,上記__expand_range.fish
関数内で呼び出しているexpand_range
関数を任意の文字列操作コマンドに変えることにより,対話モードに限って好きな構文を擬似的に追加できるからです.
すぐ思いつくところでは,以下のようなもがあります.
- コマンドラインに
G
を見つけたら| grep
に置換 (zshのglobal alias
相当) - 最初の語が実行可能でないファイルのとき,拡張子を見て適当なコマンドを前置 (zshの
suffix alias
相当) -
!!
をeval $history[1]
に置換 (ヒストリ置換相当) - コマンドラインが特定の条件を満たすとき,空白を前置してヒストリに残さない (
bash
のHISTIGNORE
相当) - コマンドライン中の重複した空白を一つにしてから実行 (
zsh
のhist_reduce_blanks
相当) -
$((...))
を(math ...)
に置換 -
&&
を; and
に,||
を; or
に置換
もちろん,上記全部を実装するくらいなら初めからzsh
を使え,という話になるのですが,「fishには概ね満足だが,他のシェルにあるあの機能がないのが我慢できない」という場合は,必要な機能だけつまみ食いして追加できる可能性が,あるのではないでしょうか...
追記(2017/4/1)
改行へのキーバインドによってグローバルエイリアスを実現する,を既にやっている方がおられました!
zshからfishに乗り換えてみた
-
キーバインドで一つの関数を呼び出すのではなく,
;
で分割して複数コマンド呼び出しているのは,関数が削除された場合のための保険です.他のキーならともかく,改行を失うことになると大変なので. ↩