私はシステムからファイルを検索し、各ファイルに対していくつかの完全性チェックを実行するスクリプトを書いています。検査に合格したら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
pv
1秒あたり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 -r0
)xargs
ボーナスで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
。