fzfプロジェクトを動的に更新

fzfプロジェクトを動的に更新

私はシステムからファイルを検索し、各ファイルに対していくつかの完全性チェックを実行するスクリプトを書いています。検査に合格したらfzfに表示したいです。 fzfでエントリをクリックすると、プログラムでファイルを実行したいと思います。

これまで私は以下を持っています:

dir="/path/to/dir"

fd . $dir --size +1MB | while read -r line; do
    file_type=$(file -b "$line")
    echo "$line" | fzf
    if [[ "$file_type" == "data" ]]; then
        echo "$file_type"
    fi
done

デフォルトでは、指定されたディレクトリから1MBを超えるファイルを検索します。各ファイルに対して file コマンドを実行し、コマンド出力が「data」を返すことを確認します。その場合は、fzfリストに追加したいと思います。次に、エントリをクリックすると、アプリケーションを使用してフルパスでファイルを実行したいと思います。例: myapp /path/to/file/selected/in/fzf

これまでの問題は、fzfブロックと私がループのリストを埋めることができないということです。本当にすべてを配列に最初に追加してからfzfにパイプする必要がありますか?理想的には、これは並列に発生する必要があります。つまり、検索が完了するのを待たずに検索中に動的に新しいアイテムを追加したいと思います。

また、後で選択したファイルを実行する方法もわかりません。誰かが私を助けることができますか?

ベストアンサー1

fzfあなたが考える方法を妨げないでしょう。リストに動的に追加できます。次の例ではこれを確認できます(pvインストールされていない場合は最初にインストール)。

find / | pv -qlL 1 | fzf

pv1秒あたり1行を出力するこの行が表示され、ブロックせずにfzf選択項目を上下に移動できます。

コードの問題は、ループfzf内で実行されていることですwhile read …。各行に対してfzfその行のみを取得する別のコードを実行します。ループはfzf完了後にのみ続行できます。ですから、それ以上の読みを拒否するのではなく、さらにfzf読みを拒否することです。fzfループの内側にあるということです。

基本的には次のようなものが欲しい。

find … | data_filter | fzf

その中にはdata_filter行をフィルタリングするコードがあります。シェルループかもしれません。

find … | while IFS= read -r line; do …; done | fzf

フィルタとして、ループはフィルタリングしたくないすべての行(およびその行のみ)を標準出力として印刷する必要があります。名前付きシェル関数にすることができますdata_filter。あなたの場合、これは正しい機能です。

data_filter() {
   while IFS= read -r line; do
   [ "$(file -b "$line" 2>/dev/null)" = data ] && printf '%s\n' "$line"
   done
}

その後、これはパイプのフィルタとして使用されます。

find … | data_filter | fzf今、私たちはうまくいき、選択したパス名を印刷するパイプを持っています。ファイルに対して特定のタスクを実行するには、次のいずれかのコマンドを使用します。

  • find … | data_filter | fzf | xargs …xargs行全体をそのまま読み取るように構成されています。 GNUの場合、xargs実行したいツールが次のような場合ls -l

    find … | data_filter | fzf | xargs --no-run-if-empty -d '\n' -I{} ls -l {}
    

    ただし、xargsデフォルトでは引用符を解釈して他の操作(分割など)を実行するため、このような状況で使用するにはそのオプションをよく理解する必要があります。私はそれを習得したことがなく、xargsここで最高のオプションセットを使用しているかどうかはわかりません。私のポイントは次のとおりです。このような単純な呼び出しは… | fzf | xargs ls -l多くの場合中断されます。

  • f="$(find … | data_filter | fzf)""$file"、お好みのどこでも使用してください。 1つの利点は、終了状態がわかるということですfzf。理論的な欠点は、$()末尾の改行が取り除かれることです。実際、私たちの場合、末尾(またはランダム)改行文字を含むパス名はとにかくうまく渡されませんdata_filter | fzf

    例:

    f="$(find . | data_filter | fzf)"
    status="$?"
    [ "$status" = 0 ] && ls -l "$f"
    

パイプライン内のすべてのツールは改行を使用して項目を区切るため、改行のあるパス名はコードを破損します。


改行を含むパス名を処理するには、パイプライン全体で(改行ではなく)NULLで終わるエントリを使用する必要があります。

  • まずfind … -print0(または同等のfdコマンド)が必要です。一部の実装ではfindこれをサポートしていません-print0-exec printf '%s\0' {} +交換可能)。

  • これにより、新しいフィルタ機能は次のようになります。

    data_filter0() {
       while IFS= read -r -d '' line; do
       [ "$(file -b "$line" 2>/dev/null)" = data ] && printf '%s\0' "$line"
       done
    }
    
  • 次回使用してくださいfzf --read0 --print0

  • 最後にxargs -r0 …。 (代替案は面倒なので、詳しくは説明しません。$()この場合はこれが優先されます。xargs -r0xargsボーナスでxargs -r0選択式もうまく機能しますfzf -m

パイプラインの例:

find . -print0 | data_filter0 | fzf -m --read0 --print0 | xargs -r0 ls -l

注意事項と便利なリンク:

  • 通常、フィルタを埋め込むことができますfind …(ありがとうfind -exec)。あなたが使用したいと思うし、fdツールについてはよくわからないので、そうしませんでした。

  • アイテムを早期に選択する(つまり、findフィルタが完了する前に)、fzfパイプラインの後のアイテムがfind終了する前に終了できます。フィルタは、fzf書き込みを試みた後にはもう存在しないことがわかります。同様findに、書き込みを試みた後にわかります。この回答)。これはfind、操作が必要以上に長くかかることを意味し、これはシェルがスクリプト内の次のコマンドを続行しないようにします(または対話型の場合はプロンプトが表示されなくなります)。バックグラウンドでいくつかの部分を実行できます。

    ( find . -print0 | data_filter0 & ) | fzf -m --read0 --print0 | xargs -r0 ls -l
    

    find終了後、フィルタの動作は停止しませんが、fzfライン全体が停止しません。シェルはls操作が完了したらすぐに移動します。バックグラウンドプロセスは近いうちに終了します。

  • 正しい引用。あなたの質問に引用された内容はありません$dir

  • なぜprintfより良いですかecho

  • 理解するIFS= read -r line

  • BashでNULLバイトを使用する方法は?

おすすめ記事