履歴をたどるコマンドラインに自分のコードをどのように配置できますか?

履歴をたどるコマンドラインに自分のコードをどのように配置できますか?

さまざまなテキストファイルに、何百ものテストされ試みられたコマンドとフラグメントが格納されています。再利用する必要がある場合は、見つけてコマンドラインにコピーして貼り付けます。信頼できますが、これは不器用なアプローチです。これとは対照的に、履歴ファイルから以前のコマンドをコマンドラインで直接検索して、編集および使用の準備をすることができます。この機能をエミュレートして独自のコマンドセットを使用するにはどうすればよいですか?

fzfを使って検索し、私のコマンドを見つけて履歴ファイルに追加するスクリプトがあります。これは素晴らしい作品です。しかし、それは歴史的な文書を混乱させる。

これは私のスクリプトです。

find ~ -type f | fzf --preview-window=up:50% --header "ENTER:less; ^s:snip to history" --bind "enter:execute(less {})"  --bind "ctrl-s:execute(cat {} | fzf -m --preview-window=:hidden  >>~/.bash_history)"; history -n

ベストアンサー1

インタラクティブBashのための迅速で簡単な設定(my bash --versionprint 5.2.15):

  1. 必要な(1行)コマンドを~/.commands

  2. 次のヘルパー関数を定義します。

    _get_command () {
       READLINE_LINE="$(<~/.commands fzf -1 --query="$READLINE_LINE")" &&
       READLINE_POINT="${#READLINE_LINE}"
    }
    
  3. キーへのバインディング(キーシーケンス):

    bind -x '"\C-x\C-r":_get_command'
    
  4. Ctrl+ xCtrl+で始めましょうr

メモ:

  • READLINE_POINT="${#READLINE_LINE}"カーソルは最後に配置する必要があります。 Bash 5は文字の長さを望み、上記のコードは機能しますが、Bash 4はバイトが必要です。 Bash 4で非ASCIIテキストを処理するには、次のようにします。

    READLINE_POINT="$(printf '%s' "$READLINE_LINE" | wc -c)"
    

    (私は - から来ましたこの回答)。

  • ~/.commandsこれは、各コマンドの目的のカーソル位置を指定またはエンコードするために独自のフォーマットを作成できるためです。あなたのフォーマットに従って設定できるようにヘルパー機能が強化されましたREADLINE_LINEREADLINE_POINT

    一方、~/.commands保存専用リテラルコマンドは次のとおりです。シンプルそして--query="$READLINE_LINE"

  • 複数行コマンドの場合fzf --read0と null で終わる項目が必要です~/.commands。この形式を維持することは、改行文字で終わる項目を編集するよりも困難です。


上記を作成した後、この機能が必要だと判断しました。より複雑な解決策は次のとおりです~/.bashrc

export SNIPPETFILE

_del_snippet () {
   SNIPPETFILE="${SNIPPETFILE:-"$HOME/.snippets"}"
   printf '%s\0' "$1" | sort -zm -- "$SNIPPETFILE" - | uniq -zu >"$SNIPPETFILE"~ \
   && mv -- "$SNIPPETFILE"~ "$SNIPPETFILE"
   cat -- "$SNIPPETFILE"
}
export -f _del_snippet

_get_snippet () {
   SNIPPETFILE="${SNIPPETFILE:-"$HOME/.snippets"}"
   local line
   line="$(<"$SNIPPETFILE" fzf -1 --read0 --query="$READLINE_LINE" \
      --border top --border-label 'Enter to confirm, Ctrl+y to delete entry, Ctrl+c to abort' \
      --bind 'ctrl-y:reload-sync(exec bash -c "_del_snippet \"\$1\"" bash {})')" \
   && READLINE_LINE="$line" \
   && READLINE_POINT="${#READLINE_LINE}"
}
bind -x '"\C-x\C-r":_get_snippet'

_put_snippet () {
   [[ "$READLINE_LINE" =~ ^[[:space:]]*$ ]] && return 0
   SNIPPETFILE="${SNIPPETFILE:-"$HOME/.snippets"}"
   if
      touch -- "$SNIPPETFILE" \
      && printf '%s\0' "$READLINE_LINE" | sort -zum -- "$SNIPPETFILE" - >"$SNIPPETFILE"~ \
      && mv -- "$SNIPPETFILE"~ "$SNIPPETFILE"
   then
      printf 'command stored in `%s'\''\n' "$SNIPPETFILE" >&2
   else
      printf 'storing in `%s'\'' failed\n' "$SNIPPETFILE" >&2
      return 1
   fi
}
bind -x '"\C-x\C-m":_put_snippet'

使用法:

  • Ctrl+ xCtrl+を使用して現在のコマンドラインを保存しますm

  • Ctrl+ xCtrl+を使用して保存したrコマンドのリストから選択します。すでに入力した内容と正確に一致するものがある場合は、コマンドラインに移動します。より多くの場合、インタラクティブに選択できます。

  • インタラクティブに選択すると、Ctrl+はy強調表示された項目を削除します。

メモ:

  • 複数行コマンドはサポートされていますが、正しく保存するためにクリックすることはできませんEnter(引用符の内側または後ろ\)。それ以外の場合は、セカンダリプロンプトが表示されます。履歴から複数行のコマンドをインポートするか、Ctrl+ v、+を押してコマンドをCtrl複数行にすることで、jセカンダリプロンプトが表示されずにコマンド全体を編集できます。その後、Ctrl+ xCtrl+はm複数行のコマンド全体を保存できます。

    これらの行をインタラクティブに選択すると、複数行のコマンドのようには見えませんが、複数行でコマンドラインに表示されます。

    ここには良い副作用があります。ラベル付きコマンドを保存できます。これ

    # description (label) here
    :
    actual_command args
    

    リストは次のとおりです。

    # description (label) here : actual_command args
    

    しかし、選択した後は動作します。

  • デフォルトのファイルはです$HOME/.snippetsexportという変数を設定して使用して、別のパス名を使用できますSNIPPETFILE。後にチルダがあるパス名(例$HOME/.snippets~:)もメンテナンスに使用されます。

  • Bash 5.2.15でGNUとGNUでsortテストされていますuniq(移植可能ではないオプションを使用)。

  • それでもエラーがある可能性があります。

おすすめ記事