僅僅這樣做並不能解決問題

僅僅這樣做並不能解決問題

首先要切斷瑣碎但不適用的答案:我既不能使用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都用'"以及帶和不帶$.這些似乎都沒有任何區別…


我現在沒有主意了。我又嘗試了一些事情,但似乎都遇到了所列出的相同問題。

我還能做什麼?這是可行的嗎?

當然,我可以讓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

相關內容