
首先要切斷瑣碎但不適用的答案:我既不能使用find
+xargs
技巧,也不能使用它的變體(如find
with -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
.無論如何,我更喜歡find
with-print0
和sort
with -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
都用'
和"
以及帶和不帶$
.這些似乎都沒有任何區別…
我現在沒有主意了。我又嘗試了一些事情,但似乎都遇到了所列出的相同問題。
我還能做什麼?這是可行的嗎?
當然,我可以讓program
接受模式並自行進行搜尋。但這需要大量的雙重工作,同時將其固定為特定的語法。 (grep
例如,透過 a 提供文件怎麼樣?)。
我也可以讓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
仍然讓我不知道如何處理另一個搜索......
答案1
使用數組。
如果您不需要處理檔案名稱中換行符的可能性,那麼您可以逃脫
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,您可以在陣列構造期間使用-print0
和-d ''
來以 null 終止名稱:
mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)
(對於 也類似XYZ_FILES
)。如果你不擁有較新的 bash,那麼您可以使用空終止讀取循環將檔案名稱附加到數組,例如
ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)
答案2
您可以使用 IFS=newline (假設沒有檔案名稱包含換行符),但必須在替換之前在外殼中設定它:
$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker
使用zsh
但不使用 null也bash
可以。$'\0'
即使在bash
你可以處理換行符,如果有一個足夠奇怪的字元從未像這樣使用過
IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...
但是,此方法無法處理您在 @steeldriver 的答案的註釋中提出的附加請求,即如果 find a 為空,則省略 --afiles 。
答案3
我不確定我是否明白你為什麼放棄xargs
。
因此,從一個搜索中進行搜索
xargs
仍然讓我不知道如何處理另一個搜索......
該字串--xyz-files
只是眾多參數之一,在程式解釋它之前沒有理由認為它是特殊的。我認為你可以xargs
在兩個find
結果中傳遞它:
{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files