管道 glob 到 ls
文件內容
filelist
:/some/path/*.txt /other/path/*.dat /third/path/example.doc
我想列出這些文件,所以我這樣做:
cat filelist | xargs ls
但我沒有擴大這些球體,而是得到:
ls: cannot access '/some/path/*.txt': No such file or directory ls: cannot access '/other/path/*.dat': No such file or directory /third/path/example.doc
貝殼擴大球體。在這裡,這是非常罕見的情況之一,其中隱式 split+glob 運算符在 zsh 以外的類似 Bourne 的 shell 中不帶引號的命令替換時呼叫可能很有用:
IFS=' ' # split on newline only set +o noglob # make sure globbing is not disabled ls -ld -- $(cat filelist) # split+glob
在
zsh
中,你會這樣做:ls -ld -- ${(f)~"$(<filelist)"}
f
要在換行符上拆分的參數擴展標誌在哪裡,並~
請求萬用字元,否則預設情況下不會在參數擴展或命令替換時完成。請注意,如果匹配文件的列表很大,您可能會遇到Argument list too long錯誤(
execve()
大多數係統上系統呼叫的限制),xargs
否則可以解決此問題。在zsh
中,您可以zargs
改用:autoload zargs zargs --eof= -- ${(f)~"$(<filelist)"} '' ls -ld --
Where
zargs
將拆分列表並執行ls
多次以避免必要的限制xargs
。或者您可以將列表傳遞給內置命令(因此不涉及
execve()
系統呼叫):要列印文件列表:
print -rC1 -- ${(f)~"$(<filelist)"}
或者將其提供給
xargs
NUL 分隔:print -rNC1 -- ${(f)~"$(<filelist)"} | xargs -r0 ls -ld --
請注意,如果任何 glob 無法匹配文件, in
zsh
,您將收到錯誤消息。如果您希望將這些 glob 擴展為空,您可以將N
glob 限定符添加到 glob(這nullglob
在每個 glob 的基礎上啟用):print -rNC1 -- ${(f)^~"$(<filelist)"}(N) | xargs -r0 ls -ld --
添加
(N)
它還會將所有沒有 glob 運算符的行轉換為 glob,從而允許過濾掉路徑引用且不存在的文件;但是,這意味著您不能在 glob 中使用 glob 限定符,filelist
除非您將它們表示為(#q...)
並啟用該extendedglob
選項。另請注意,由於限定符可以執行任意程式碼,因此filelist
文件的內容來自受信任的來源很重要。在其他類似 Bourne 的 shell 中,包括
bash
,不匹配的 glob 保持原樣,因此將按字面意思傳遞給ls
可能會報告相應文件不存在的錯誤。在
bash
中,您可以使用該nullglob
選項(它從 zsh 複製)並處理沒有任何 glob 特別匹配的情況:shopt -s nullglob IFS=$'\n' set +o noglob set -- $(<filelist) (( $# == 0 )) || printf '%s\0' "$@" | xargs -r0 ls -ld --
bash
, 沒有任何等價於zsh
’ 的 glob 限定符。為了確保沒有 glob 運算符的行(例如 your/third/path/example.doc
)被視為 glob 並在它們不對應於實際文件時將其刪除,您可以添加@()
到行中(requiresextglob
)。/
但是,這對於以字元結尾的行不起作用。但是,您可以添加@()
到最後一個非字元並依賴於始終存在/
的事實/
shopt -s nullglob extglob IFS=$'\n' set +o noglob set -- $(LC_ALL=C sed 's|.*[^/]|&@()|' filelist) (( $# == 0 )) || printf '%s\0' "$@" | xargs -r0 ls -ld --
在任何情況下,請注意支持的 glob 運算符列表隨 shell 變化很大。不過,您在範例中使用的唯一一個 (
*
) 應該得到所有人的支持。