コマンドラインをviモードで編集するのエントリで「それどうやんの!?」と訊いてきた知人から、後日こんなメールをもらった。
viモードにしてその延長上でコマンドの結果に対してpromptで編集(?)みたいのってできるのですかね?
例えば
$ cat somefile.txt # これにファイル名が幾つか入っている src/file1.txt src/file2.txtでここでESCを押すとviモードになって
j
とかk
とかでfile1.txt
とfile2.txt
がチラチラと見える。
最終的にはi
とか使って$ vi src/file2.txtとかがぱっとできたりするといいなーと思っている。
今までそういう発想をしたことがなかったが、欲しいシチュエーションは確かにありそうだ。
標準出力の結果をvimで編集することを前提にするならば、
$ cat somefile.txt | vim -R -
とやって、vim内でsrc/file1.txt
にカーソルを合わせて
src/file1.txt src/file2.txt
gf
とタイプすれば、src/file1.txt
がそのまま編集可能になる。
蛇足だが、somefile.txt
が仮に
src src/file1.txt src/file2.txt
となっていて、src
にカーソルを合わせてgf
とタイプすれば、vimの中でディレクトリビューが表示される。
もちろん、カーソルの移動はvimのキーバインド。ファイル名やディレクトリ名(../
も含む)にカーソルを合わせてEnterを押せば、そのファイルやディレクトリを開くことができる。別のファイルを開くときに、いちいち:q
コマンドでvimを抜けてディレクトリを移動しなくて済むし、:e
コマンドや:sp
コマンドで別ファイルを開くときも、ディレクトリ名まで補完した状態でEnterを押せば、同じようにディレクトリビューが使える。
さて、話を戻そう。
コマンド履歴が保存されている場所として真っ先に思いつくのが~/.bash_history
ファイル。標準出力の結果を~/.bash_history
に追記すれば実現できるかもと思ったが、~/.bash_history
はログアウト時(ターミナルでexit
した時)に書き出されて、ログイン時(ターミナルを開いた時)に読み込まれる。セッション中のコマンド履歴はbashのメモリ内で保持されていて、どこかのファイルに書き出されているわけではない。
historyコマンドを使ってコマンド履歴の書き出し、読み込みはできるが、直接編集できるようなオプションはない。
$ cat somefile.txt > tmp $ history -r tmp $ rm -f tmp
とやれば、とりあえずコマンド履歴に追記することはできるが、いちいちテンポラリファイルを手動で作って消してとやるのは非効率的。~/.bashrc
あたりにサブルーチン化してしまうのが得策だろう。
で、addhist
というサブルーチンを書いてみた。
addhist() { local tmpfile=`mktemp` while read cmd do [ -n "$cmd" ] && echo $cmd >> $tmpfile done history -r $tmpfile rm -f $tmpfile }
とりま~/addhist
として保存して試してみる。
$ . ~/addhist $ history -c $ cat somefile.txt | addhist $ history 1 cat somefile.txt | addhist 2 history
あれ?おかしいな。
デバッグ用に一行追加。
addhist() { local tmpfile=`mktemp` while read cmd do [ -n "$cmd" ] && echo $cmd >> $tmpfile done history -r $tmpfile rm -f $tmpfile history }
$ . ~/addhist $ history -c $ cat somefile.txt | addhist src/file1.txt src/file2.txt
addhist
の中ではちゃんと履歴に追加されている。
あ~!パイプで渡してるからaddhistが別セッションになってしまってるわけね。呼び出し元のセッションには何の影響もないと。addhist
内で書き込んだhistoryをexportすることはできるのだろうか…頼りのGoogle先生が出した答えがコレだ。
Real-time history export amongst Bash terminal windows(2012/08/21 リンク切れ)
なるほど。$PROMPT_COMMAND
を使って、$HISTFILE
(=~/.bash_history
)経由で全セッションで同期させるのね。これができるなら、もうaddhist
は存在自体が否定されてしまうような…。
ひとまず、~/.bashrcに以下の数行を追記して、
shopt -s histappend export HISTCONTROL=ignoreboth:erasedups export HISTSIZE=100000 export HISTFILESIZE=$HISTSIZE export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
bashの別セッションを開く。$PROMPT_COMMAND
が再帰してるので、テスト段階では.
(ドット)コマンドで読み込むと面倒なことになる。
$ bash $ > $HISTFILE $ history 1 > $HISTFILE 2 history $ cat somefile.txt >> $HISTFILE $ history 1 > $HISTFILE 2 src/file1.txt 3 src/file2.txt 4 cat somefile.txt >> $HISTFILE 5 history
おお、期待通りsomefile.txt
の内容がhistoryに追加できた。
でも個人的には
$ cat somefile.txt >> $HISTFILE
とリダイレクトを使うより、addhist
みたいにパイプでつなぐ方がいい。理由は2つあって、1つは$HISTFILE
のキーストロークがShift押しっぱなしで疲れる。もう1つはリダイレクトの>>
をうっかり>
とtypoしてしまうと履歴がきれいに消えてしまうという悲惨な結果になる。これは結構ありがちなパターンかと。
ということで、終盤にきてaddhist
復活!
最終的に~/.bashrc
に追記したコードは以下の通り。
shopt -s histappend export HISTCONTROL=ignoreboth:erasedups export HISTSIZE=100000 export HISTFILESIZE=$HISTSIZE addhist() { while read cmd do [ -n "$cmd" ] && echo $cmd >> $HISTFILE done } export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
これ超便利っす!
ありがとうございました!
便利なとき
svn st で新しいファイルがあるときに
? very/long/folder/path/newfile.txt
なんて表示されているので
svn st | addhist
やってhistoryをさかのぼって
svn add very/long/folder/path/newfile.txt
できるのがすごーくうれしいです。
以前のTIP
set -o vi
と組み合わせて使うとマウスいらずで超はやい!