LoginSignup
2
6

More than 5 years have passed since last update.

ミスって0秒で即fuck ~ 自動fuckしたいだけの人生だった

Posted at

英国王のスピーチを見てたら思い出した。
何事もタイミングですな。

結果的には30%程度の完成率なので、だれか先帝の無念を晴らしてください。
王か帝か、いやいやITエンジニア。

autofucksというとカー用品店みたい。


コマンドに失敗した瞬間、脊髄反射かのように自動でfuckを唱えたい。
なぜならfuckと思った瞬間にはfuckは口から出ているべきものだと思うからです。

thefuck導入

できるエンジニアはthefuckしてる - Qiita

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

fuckfunctionの中身をコピペし、変数fucked_up_commandをhistoryから$argvへ。
ついでに完了後のhistory操作も削除しておいた。先のままでは無関係なhistoryが消えていくので。

で、使ってみると幾つかの問題点が浮かび上がり、故に完成度は30%ぐらいに思える。

問題点

前提として、普通のコマンド(評価時に処理が完了する系)は上手くいく。

  • historyに誤ったコマンドのみが残る
    • evalのfuckしたコマンドは履歴に残らず、失敗コマンドの履歴登録よりも前に処理するので失敗コマンドも削除できない。
    • 実行する$unfucked_commandをhistoryに登録することはできると思うが、その場合も 修正したコマンド → 修正前の失敗コマンド という登録になると思うので、ビミョウ
  • 終了時ステータスが更新されない
    • fishで言う$status。この値が、fuckで出た正常なコマンド実行後も0にならない。
  • vimやlessといった対話モード?(インタラクティブシェル?)になるようなコマンドは、バックグラウンドに移動してしまいます。イベント駆動でない通常のfuckなら問題無しなのに。

解決策や原因調査も手に余りそうなので一旦投げます。
どなたか快適なfuckを目指しませんか。すでにあっても嬉しい。

動作サンプル

ということで、cdmkidrなど単純なコマンドならば…!状態。

autofuck.gif

妥協案

発想の転換で問に解"ものは見方"

そもそも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使わないのかな。

2
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6