英国王のスピーチを見てたら思い出した。
何事もタイミングですな。
結果的には30%程度の完成率なので、だれか先帝の無念を晴らしてください。
王か帝か、いやいやITエンジニア。
autofucksというとカー用品店みたい。
コマンドに失敗した瞬間、脊髄反射かのように自動でfuckを唱えたい。
なぜならfuckと思った瞬間にはfuckは口から出ているべきものだと思うからです。
thefuck導入
nvbn/thefuck: Magnificent app which corrects your previous console command.
yum install python-devel
pip install thefuck
fish -c 'thefuck --alias > ~/.config/fish/functions/fuck.fish'
thefuck --alias
の中身はこんな感じ
function fuck -d "Correct your previous console command"
set -l fucked_up_command $history[1]
env TF_ALIAS=fuck PYTHONIOENCODING=utf-8 thefuck $fucked_up_command | read -l unfucked_command
if [ "$unfucked_command" != "" ]
eval $unfucked_command
builtin history delete --exact --case-sensitive -- $fucked_up_command
builtin history merge ^ /dev/null
end
end
直前の履歴を使ってfuckして、その履歴は消しちゃう。と読んでいる。
誤ったコマンドは消すので残らないが、さりとて直したコマンドも残らない、サイレントfuck(fuckは残るが)
fish functionのお勉強
オートロードはイベントハンドラー には使えません。
なぜなら関数がロードされていないときは、イベントが起きたときに、その関数が実行されるべきであるとfishは知ることができないからです。
詳細はイベントハンドラーの節を参照してください。
fish2.4日本語ドキュメント:すぐ使える25の便利機能と実例:オートロード関数
注意!イベントハンドラーは関数がロードされたときのみ有効になります。
オートロード するのではなくて source コマンドを使うか関数を実行する必要があります。
~/.config/fish/config.fish に記述するのがひとつの方法です。
fish2.4日本語ドキュメント:すぐ使える25の便利機能と実例:イベントハンドラー
イベントハンドラのオプションの
ひとつが関数に与えられたとき、
関数は特定のイベントが発生したときに
自動で実行されます。
~ 中略 ~
fish_command_not_found
コマンド検索に失敗したときに発生する。
function:【中核】イベントハンドラも別名も関数定義!
まさにfuckしろと仰っているかのようなイベント。
その1:動かしてみよう
教えに従い、~/.config/fish/config.fish
にfunctionを書く。/functions でないと違和感ある。
function autofuck --on-event fish_command_not_found
fuck
end
潔い。
config.fish
に書く関係上、無名関数がよかったが、zshはできてfishはできないみたい?
問題点
さっそく使ってみると、すぐに目立つ問題点が出た。
- デフォルトの
fish: Unknown command 「コマンド」
が出力される。(問題ではないが、制御が想定外) - イベントタイミングがhistoryの登録より前らしく、fuck対象が失敗したコマンドにならない。(その一つ前のhistoryコマンドを処理する)
その2:デフォルト出力を消そう
本丸の前に、
fish: Unknown command 「コマンド」
はどこから来るのか。
ネットで調べたら出てきた。
が、fishはfなので、functions -a
からそれらしいものを探すといういちステップを挟んでもよかった。
What's your functions name? Reading type
__fish_command_not_found_setup
, it seems to define a function__fish_command_not_found_handler
, so if you want to stop that function from executing, you should be able to erase or simply override it.
Custom event handlers · Issue #2231 · fish-shell/fish-shell
つまり、自作とデフォルト両方がイベントで動いているから、デフォルトの方を消すかオーバーライドしちゃいなYO。
こういう時にfishの良い機能なんですが、functions 名前
で関数の中身も見えるのが良い1
> functions __fish_command_not_found_handler
function __fish_command_not_found_handler --on-event fish_command_not_found
__fish_default_command_not_found_handler $argv[1]
end
> functions __fish_default_command_not_found_handler
function __fish_default_command_not_found_handler
echo "fish: Unknown command '$argv'" >&2
end
ちょうど自作の方の関数の名前に悩んでいたので、オーバーライドすることに。
~/.config/fish/config.fish
の方を編集して名前を変える
function __fish_command_not_found_handler
--on-event fish_command_not_found
fuck
end
fish: Unknown command
が出ないことを確認できればひとまずOK。
その3:失敗したコマンドをfuck対象にしよう
historyには失敗イベントが発生したコマンドがまだ書き込まれていないので、なんとかコマンドを取得せねばならない。
ここで、先のデフォルトの処理を思い出してみると
> functions __fish_command_not_found_handler
function __fish_command_not_found_handler --on-event fish_command_not_found
__fish_default_command_not_found_handler $argv[1]
end
> functions __fish_default_command_not_found_handler
function __fish_default_command_not_found_handler
echo "fish: Unknown command '$argv'" >&2
end
イベント駆動でも$argv
にトリガーとなったコマンドが渡されているように見える。
デバッグで$argv
を出力してみると、打ったコマンドが全て入っていたので、これを処理対象にした。
function __fish_command_not_found_handler --on-event fish_command_not_found
#echo $argv
set -l fucked_up_command $argv
env TF_ALIAS=fuck PYTHONIOENCODING=utf-8 thefuck $fucked_up_command | read -l unfucked_command
if [ "$unfucked_command" != "" ]
eval $unfucked_command
end
end
fuck
functionの中身をコピペし、変数fucked_up_command
をhistoryから$argv
へ。
ついでに完了後のhistory操作も削除しておいた。先のままでは無関係なhistoryが消えていくので。
で、使ってみると幾つかの問題点が浮かび上がり、故に完成度は30%ぐらいに思える。
問題点
前提として、普通のコマンド(評価時に処理が完了する系)は上手くいく。
- historyに誤ったコマンドのみが残る
- evalのfuckしたコマンドは履歴に残らず、失敗コマンドの履歴登録よりも前に処理するので失敗コマンドも削除できない。
- 実行する
$unfucked_command
をhistoryに登録することはできると思うが、その場合も 修正したコマンド → 修正前の失敗コマンド という登録になると思うので、ビミョウ - 終了時ステータスが更新されない
- fishで言う
$status
。この値が、fuckで出た正常なコマンド実行後も0にならない。 - vimやlessといった対話モード?(インタラクティブシェル?)になるようなコマンドは、バックグラウンドに移動してしまいます。イベント駆動でない通常のfuckなら問題無しなのに。
解決策や原因調査も手に余りそうなので一旦投げます。
どなたか快適なfuckを目指しませんか。すでにあっても嬉しい。
動作サンプル
ということで、cd
やmkidr
など単純なコマンドならば…!状態。
妥協案
発想の転換で問に解"ものは見方"
そもそもfuckしてコマンド選んだときにそのままeval
されるのが悪い。
普通のfuckでも違和感を覚えていた。
原因はたぶん、peco。
選んだものはコマンドラインに出力するようにしていて、historyでも同じコマンドを使う際にも選択してからエンターのワンクッションが必要。
操作は増えるが、コマンドを改変して実行などにはよく助かる。
これに慣れすぎてfuckが妙な感じならば、fuckの結果もコマンドラインに出せばいいじゃない。
ワンアクション必要になるし、自動fuckとはいえないかもしれないけれど、fuckとは言わないしhistoryにも修正後のコマンドが失敗したコマンドの後に残るし、コマンドラインからの新規実行だからlessやvimも問題無いし、peco系のツールとも動作が似通うし、まあいいんじゃないかなと投稿直前に思いました。
なので、コードだけ。
function __fish_command_not_found_handler --on-event fish_command_not_found
# やっぱりなぜfuckするのか出ないと妙なので残すことにした
__fish_default_command_not_found_handler $argv[1]
set -l fucked_up_command $argv
env TF_ALIAS=fuck PYTHONIOENCODING=utf-8 thefuck $fucked_up_command | read -l unfucked_command
if [ "$unfucked_command" != "" ]
#eval $unfucked_command
commandline $unfucked_command
end
end
記事にfuck書きまくるの楽しい半分消耗半分だ…。
zshにはcorrect機能があるようだけど、zshの人はthefuck使わないのかな。