カーソル位置のn個前のトークンとか、先頭からn個目のトークンとかを展開できるとcp
コマンドとかで便利!と思ってabbrを実装しました。
,[+-]?[0-9]+
というパターンに反応して、符号の有無と数値を基準に、うまいことトークンを展開できます。
# 展開前
cp /path/to/file ,-1
# 展開後
cp /path/to/file /path/to/file
Fish向けの実装ですが、Zshで似た機能にはcopy-prev-word
やcopy-prev-shell-word
があります。
https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#index-copy_002dprev_002dshell_002dword
abbr概要
Fishのabbrはaliasの強化版といった感じで、短縮入力をスペースやエンターと共に本来のコマンドに展開してくれる機能です。
たとえば
abbr -a g git
とすると、gと入力してスペースを押すとgitに展開されます。
さらには--command
オプションを使ってgit a
をgit add
にするような、サブコマンドの展開も可能です。
abbr --command=git -a a add
とここまでは過去の記事でも紹介した通り。
Fish 4のabbrはサブコマンドも展開できるぞ
https://blog.atusy.net/2025/03/29/fish-4-abbr/
コマンドライン上のトークンを複製するabbr
方向性
今回の記事では、この機能を利用して、コマンドライン上の任意のトークンを展開するabbrを実装してみましょう。以下の,
はプレフィックスで使いやすいものならなんでもいいと思います。
,-[0-9]+
- カーソルから左側にn番目のトークンを展開
,[+][0-9]+
- カーソルから右側にn番目のトークンを展開
,[0-9]+
- 左端から数えてn番目のトークンを展開
1の,-[0-9]+
は特に使いどころがわかりやすいかなと思います。たとえば、直前のトークンを展開すると、cp
コマンドでコピー対象のパスを複製できるので、コピー先のパスを作りやすくなります。
# 展開前
cp /path/to/file ,-1
# 展開後
cp /path/to/file /path/to/file
3の,[0-9]+
はたとえば、ffmpeg
などで引数を大量に指定している場合に、-i
で指定した入力ファイルのパスを再利用して出力ファイルのパスを決めたい、なんてケースで効果を発揮するでしょう。
,3
とすることで、3番目のトークンであるファイルパスを展開できます。
# 展開前
ffmpeg -i path/to/file.mpg -vf "..." -loop 0 ,3
# 展開後
ffmpeg -i path/to/file.mpg -vf "..." -loop 0 path/to/file.mpg
2の,[+][0-9]+
は一番使いどころが少ないかもしれませんが、一通りの機能を揃えておくために実装しました。
実装
中身は3つの要素でできてます。
- abbr定義
- これがないと始まらないやつ
- 複雑な展開をするため、以下のオプションを指定する
--regex ',[-+]?[0-9]+'
で展開対象を動的に判定する--function __abbr-token
で関数の出力結果を使って展開できるようにする--position anywhere
でコマンドライン上の任意位置で展開可能にする
__abbr-token
関数- abbrコマンドに指定する展開用の関数
- 実態は
__abbr-token-baes
関数のラッパー - 展開結果が1トークンとして成立するように
string escape
している
__abbr-token-base
関数- abbrを展開する関数の本体部分
- 展開元(
,[-+]?[0-9]+
)の符号の種類や数値に基いて、展開結果を出力する
abbr -a token --regex ',[-+]?[0-9]+' --function __abbr-token --position anywhere
function __abbr-token
__abbr-token-base | string escape
end
function __abbr-token-base
commandline --cut-at-cursor | read --tokenize --list --local tokens_left
set -l idx ( echo $tokens_left[-1] | string sub --start 2 )
# idxが負ならカーソル位置からidx個手前のトークンを展開
if echo "_$idx" | string match -q -r "^_[-]"
set -l idx2 ( math "$idx - 1" )
echo $tokens_left[$idx2]
return 0
end
commandline | read --tokenize --list --local tokens_all
# idxが+ではじまる正数ならカーソル位置からidx個手後のトークンを展開
if echo "_$idx" | string match -q -r "^_[+]"
set -l base ( count $tokens_left )
set -l idx2 ( math "$base + $idx" )
echo $tokens_all[$idx2]
return 0
end
# idxに正負の符号がない場合は全トークンからsliceする
echo $tokens_all[$idx]
end
本当は__abbr-token-base
でidx
のバリデーションを加えるべきでしょうが、実用上はabbr
の--regex
オプションで指定したパターンしか入らないので、省略してます。
ところでプレフィクスには,
を使っていますが、グローバル変数に切り出しておけば、お好みにカスタマイズもできそうです。
set -g ABBR_TOKEN_PREFIX ","
abbr -a token --regex $ABBR_TOKEN_PREFIX"[-+]?[0-9]+" --function __abbr-token --position anywhere
function __abbr-token-base
# 前略
set -l len_prefix (string length $ABBR_TOKEN_PREFIX)
set -l idx ( echo $tokens_left[-1] | string sub --start $len_prefix )
# 後略
end