Bash

如何將 find 找到的文件作為參數傳遞?

  • October 25, 2016

首先要切斷瑣碎但不適用的答案:我既不能使用find+xargs技巧也不能使用它的變體(如findwith -exec),因為每次呼叫我都需要使用很少的這樣的表達式。我會在最後回到這個。


現在舉一個更好的例子,讓我們考慮:

$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

我如何將這些作為參數傳遞給program?

只是這樣做並不能解決問題

$ ./program $(find -L some/dir -name \*.abc | sort)

program由於獲得以下參數而失敗:

[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc

可以看出,帶有空格的路徑被分割並program認為它是兩個不同的參數。

報價直到它有效

似乎像我這樣的新手使用者,在遇到此類問題時,往往會隨機添加引號,直到它最終起作用 - 只是在這裡它似乎沒有幫助……

"$(…)"

$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

因為引號防止分詞,所以所有文件都作為單個參數傳遞。

引用單個路徑

一個有前途的方法:

$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"

引號在那裡,當然。但它們不再被解釋。它們只是字元串的一部分。所以他們不僅沒有阻止分詞,而且還陷入了爭論!

更改 IFS

然後我試著玩弄IFS. 無論如何,我更喜歡findwith-print0sortwith -z- 這樣他們自己就不會在“有線路徑”上遇到問題。那麼為什麼不對null角色強制分詞並擁有一切呢?

$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc

所以它仍然在空間上分裂,而不是在null.

我嘗試將IFS作業放置在$(…)(如上所示)和之前./program。我還嘗試了其他語法,例如\0, \x0\x00都引用了'and"以及有和沒有$. 這些似乎都沒有任何區別……


在這裡我沒有想法。我嘗試了更多的東西,但似乎都遇到了與列出的相同的問題。

我還能做什麼?它完全可行嗎?

當然,我可以program接受這些模式並自己進行搜尋。但是,在將其固定為特定語法時,需要做很多雙重工作。(grep例如,提供文件怎麼樣?)。

我也可以program接受一個帶有路徑列表的文件。然後我可以輕鬆地將表達式轉儲find到某個臨時文件並僅提供該文件的路徑。這可以支持直接路徑,因此如果使用者只有一個簡單的路徑,則可以在沒有中間文件的情況下提供它。但這似乎不太好 - 需要創建額外的文件並處理它們,更不用說需要額外的實現了。(不過,從好的方面來說,它可以作為參數的文件數量開始導致命令行長度問題的情況下的救援……)


最後,讓我再次提醒您,find+ xargs(和類似的)技巧在我的情況下不起作用。為了描述簡單,我只展示一個論點。但我的真實情況看起來更像這樣:

$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES

因此,xargs從一個搜尋中進行搜尋仍然讓我知道如何處理另一個搜尋……

使用數組。

如果您不需要處理文件名中換行符的可能性,那麼您可以逃脫

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

然後

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

如果您確實需要處理文件名中的換行符,並且 bash >= 4.4,則可以在數組構造期間使用-print0and-d ''以空值終止名稱:

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(同樣適用於XYZ_FILES)。如果您沒有較新的 bash,那麼您可以使用以 null 結尾的讀取循環將文件名附加到數組中,例如

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

引用自:https://unix.stackexchange.com/questions/318424