動態更新 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 的文件。對於每個文件,我執行文件命令並檢查命令輸出是否返回“數據”。如果是這樣,我想將其添加到 fzf 列表中。然後,當我點擊該項目時,我想使用應用程序執行具有完整路徑的文件:例如
myapp /path/to/file/selected/in/fzf
.到目前為止的問題是 fzf 正在阻塞,我無法在循環中填充列表。我真的必須先將所有內容添加到數組中,然後將其通過管道傳輸到 fzf 嗎?理想情況下,這應該並行發生,也就是說,我想在搜尋仍在進行時動態添加新項目,而不是等待它完成。
我也不知道以後如何執行選定的文件。有人可以幫我弄這個嗎?
fzf
不會阻礙您的思維方式。它完全能夠即時添加到列表中。您可以在以下範例中看到這一點(pv
如果未安裝,請先安裝):find / | pv -qlL 1 | fzf
其中
pv
每秒輸出一行,這些行出現在 中fzf
,您可以上下移動選擇並且沒有任何阻塞。您的程式碼的問題是您
fzf
在while read …
循環內執行。對於每一行,您執行一個單獨fzf
的僅獲取該單行的行。循環只有在fzf
完成後才能繼續。所以不是fzf
拒絕閱讀更多;這是關於fzf
在循環中。基本上你想要這樣的東西:
find … | data_filter | fzf
where
data_filter
是一段過濾行的程式碼。它可以是一個 shell 循環: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
配置為按原樣讀取整行。使用 GNUxargs
,如果您要執行的工具是ls -l
,這將是:find … | data_filter | fzf | xargs --no-run-if-empty -d '\n' -I{} ls -l {}
但是因為
xargs
預設情況下會解釋引號並執行其他操作(例如拆分),所以您需要很好地了解它的選項才能在這種情況下使用它。我承認我從來沒有掌握過xargs
,我不確定我在這裡使用了最好的一組選項。… | fzf | xargs ls -l
我的觀點是:在許多情況下,一個簡單的呼叫 like會中斷。
f="$(find … | data_filter | fzf)"
,然後"$file"
在任何你想要的地方使用。一個優點是您可以知道fzf
. 一個理論上的缺點是$()
帶尾換行符。實際上,在我們的例子中,帶有尾隨(或任何)換行符的路徑名data_filter | fzf
無論如何都不能很好地通過。例子:
f="$(find . | data_filter | fzf)" status="$?" [ "$status" = 0 ] && ls -l "$f"
由於管道中的所有工具都使用換行符來分隔條目,因此帶有換行符的路徑名會破壞程式碼。
要使用換行符處理路徑名,您需要使整個管道使用以空字元結尾(與以換行符結尾相反)的條目。
- 首先,您需要
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
可能會比它需要的工作時間更長,從而阻止 shell 繼續執行腳本中的下一個命令(或顯示提示,如果是互動式的)。您可以在後台執行一些部分:( find . -print0 | data_filter0 & ) | fzf -m --read0 --print0 | xargs -r0 ls -l
這不會阻止
find
過濾器在fzf
退出後工作,但整條線不會停止。外殼將在ls
完成其工作後立即繼續執行。後台程序遲早會終止。
- 引用正確。在您的問題中有未引用的
$dir
.- 為什麼
printf
優於echo
?.- 了解
IFS= read -r line
。- 如何在 Bash 中使用空字節?