通信用バッファ ~/.vim/bf
クリップボードを使わずにvimとshellの間でテキストをやりとりできる方法が Software Design 2013年10月号で紹介されていました。dockerやvagrantにsshしてvimを利用している私にとっては好都合の技です。これを自分なりに使いやすくした方法を紹介します。
aliasとして使う
alias bf='cat > ${HOME}/.vim/bf'alias bfcat='cat ${HOME}/.vim/bf'
zshのglobal aliasとして使う
alias-gB='| bf'
vimのキーマップを設定して使う
" vim - shell 通信用バッファ
imap <leader>p<ESC>:read ${HOME}/.vim/bf<CR>i
nmap <leader>p:read ${HOME}/.vim/bf<CR>" vmap <C-b> :w!${HOME}/.vim/bf<CR> の改良↓
augroup MyBuf
autocmd!
autocmd TextYankPost * call writefile(split(string(v:event.regcontents)[2:-3],"', '"), $HOME ."/.vim/bf")" [ 2:-3 ]はリストのprefix [' と surfix ']を消すため
augroup END
注意点
split()
はwritefile()
の引数がリストを要求しているため、string->list
に変換する必要があるsplit(..., "', '")
はリストの区切りを消すため[ 2:-3 ]
はリストのprefix [' と surfix ']を消すため
雑誌上ではC-Bが使われていましたが、"1ページ戻る"デフォルトのキーバインドが潰れてしまうため、私はLeader pを使っています。(Pasteのpと近いので。)
Leaderのデフォルトはバックスラッシュですが、私の場合、LeaderをSpaceに設定しています。
vimでLeaderをSpaceに設定するキーマップlet mapleader ="\<Space>"
また、雑誌に載っていたスクリプトでは選択→yankしないと~/.vim/bf
に保存されませんでしたが、autocmdを使って改良したスクリプトでは選択→yankだけでなく、yyやyGなど、選択せずにyankした文字列も~/.vim/bf
に格納されるよう拡張しました。
使い方
shellの結果をvimにペースト
# bashなど
$ ps | bf
# zsh
$ ps B
# vim上で<Leader>p
PID TTY TIME CMD
26353 pts/5 00:00:00 ps
26354 pts/5 00:00:00 cat
26966 pts/5 00:00:03 zsh
vimで書いたコマンドをshellで実行
echo hello world
$$(bfcat)hello world
#または
$ bfcat | sh
hello world
bfの実行をalias化
これよく使いそうだからaliasにできますね。記事書きながら良いのを思いつきました。
alias bfexec='bfcat | ${SHELL}'
for i (hoge1 hoge2 hoge3) echo $i
$ bfexec # `for i ...echo $i` をzshで実行hoge1
hoge2
hoge3
$ bfexec | bf # 実行結果を~/.vim/bfに保存
hoge1
hoge2
hoge3
for i ...$i
の文法はbashでは使えず、zsh専用です。
aliasのパイプ後を${SHELL}
環境変数にすることで実行シェル、私の場合はzshで実行されます。
vimで書いたコマンドをshellに流して実行し、その結果をまたパイプ+bfで拾ってvimに再書き込み?この手順は二度手間ってやつでは?
vimで書いたコマンドをバッファ通さずvim上で実行するには?
shellとの通信用バッファを使わないので、この記事の範疇ではありませんが、気になったので調べてみると、:'<,'>!<command>
を使えばよいそうです。つまり、実行したいコマンドを書いたテキストを選択してから:.
Vimで変態テキスト処理!シェルコマンドを使い倒す
リンク先に倣って設定ファイルにLeader CRでその行または選択範囲をシェルに渡して実行するよう設定しました。
vnoremap <Leader><CR>:!${SHELL}<CR>
nnoremap <Leader><CR> V:!${SHELL}<CR>
まとめ
shell -> vim
shellの実行結果をvimに渡す。またはvimで書いたコマンドをshellに読み込むか実行する。
# vimとの通信用バッファalias bf='cat > ${HOME}/.vim/bf'# 書込alias bfcat='cat ${HOME}/.vim/bf'# 読込alias bfexec='bfcat | ${SHELL}'# 実行
shellの実行結果をvimに渡す(zshのglobal alias)
alias-gB='| bf'
vim -> shell
vimで書いたコマンドをshellに渡す
" vim - shell 通信用バッファ" vim - shell 通信用バッファ
imap <leader>p<ESC>:read ${HOME}/.vim/bf<CR>i
nmap <leader>p:read ${HOME}/.vim/bf<CR>" vmap <C-b> :w!${HOME}/.vim/bf<CR> の改良↓
augroup MyBuf
autocmd!
autocmd TextYankPost * call writefile(split(string(v:event.regcontents)[2:-3],"', '"), $HOME ."/.vim/bf")" [ 2:-3 ]はリストのprefix [' と surfix ']を消すため
augroup END
おまけ
vim上で書いたshellコマンドをvim上で実行
vnoremap <Leader><CR>:!${SHELL}<CR>
nnoremap <Leader><CR> V:!${SHELL}<CR>
tmux使えば良いんじゃ...?
tmuxのコピー機能を使って端末間のテキストコピーは以前から行っていましたが、改行文字やらshellのPS1(ホスト名とか現在ディレクトリ名)をコピーしないようにor貼付け後に削除する作業が煩雑でした。
しかし、このbfバッファを挟む方法なら純粋なテキストだけをshell-vim間でやりとりできます。
tmuxとbf alias運用法の違い
- tmuxでyank: 実行後に必要範囲を取捨選択してコピー
- bfに流し込む: 実行前にパイプ
| bf
して、バッファに流すことを予め宣言
shellからvimへ、1行程度を思いつきでvimにペーストするならtmuxの方がよいかもしれませんが、コマンドの結果を何度もvimに貼り付けたりするならbfの方が良いかもしれません。複数行をtmuxであとでまとめてvimに貼り付けるとプロンプトを消したり面倒くさいので、command | bf
→ vimでLeader p→command | bf
→ vimでLeader p繰り返します。
vimからshellへは
- 複雑なコマンドをvimで書いてから、ターミナルで結果を継続観察(
| tail -f
) - コマンドをバックグラウンドで走らせておいて後から戻りたい(
command &
してからfg
) - 対話的な実行(
rm -i path/to/file.txt
)するならvim上でshellコマンドの実行
上記のような操作はvim上でshell走らせるよりは、vimで書いたコマンドをターミナル上でbfexec
してあげたほうが良いかもしれません。tmux上でコピー→shellに貼り付けは改行文字が入ったりして経験上やっかいです。この辺は適材適所でしょう。