
當使用命令的輸出時,例如locate
以「人類可讀形式」產生路徑列表(即\
前面沒有空格)的命令的輸出,如何將它們的輸出重新導向到另一個命令?
的輸出$ locate [something]
會產生其中包含空格的路徑,這將禁止其他程式使用包含空格的路徑。例如,如果我要
$ du -h `locate *.doc`
這將對包含空格的所有檔案和目錄產生錯誤。 (將刻度線包裹在空格中不起作用)
答案1
您使用的具體原因是什麼locate
?這似乎符合您的要求:
find . -type f -name '*doc' -exec du -h "{}" \;
也就是說,如果你真的locate
如果確實想要使用像or 這樣的工具find
並將其輸入作為參數傳遞給另一個程序,您可以利用NUL
某些工具提供的分隔輸出和輸入。 locate
並且find
兩者都有一個選項(locate
's-0
和find
's -print0
),這將允許您獲得更編程友好的輸出,其xargs
設計用於讀取它是 -0
爭論:
find . -type f -name '*doc' -print0 | xargs -0 du -h
locate -0 '*doc' | xargs -0 du -h
答案2
如果您想要/需要將同一行傳遞給多個命令,則可以使用以下方法:
#!/usr/bin/env bash
T="${IFS}" # Save off the Internal Field Separator
IFS=$'\n' # Set it to newline.
while read file_line
do
echo "--"
echo "${file_line}"
echo "--"
done <<< $(locate $1)
IFS="${T} # Set it back to the original IFS.
包裹起來是個好主意任何“”中可能有空格的變數。
答案3
不加引號的命令替換($(...)
或`...`
古老的形式)會在類似 Bourne 的 shell 中呼叫 split+glob(僅在 zsh 中分割)。
預設情況下,分割是針對$IFS
包含空格、製表符和換行符(以及 zsh 中的 NUL)的特殊參數的字元完成的,這解釋了為什麼您的命令不適用於包含空格字元的檔案名稱。
你可以這樣做:
(IFS='
' # split on newline only
set -o noglob # disable glob
du -hc -- $(locate '*.doc'))
但如果檔案名稱包含換行符,這仍然會失敗。的輸出locate
根本無法進行後處理。
大多數locate
實作都有一個-0
選項來輸出檔案路徑NUL
- 分隔。 NUL 是唯一不能出現在檔案路徑中的位元組值,這表示輸出是可後處理的。您只需要按 NUL 進行拆分即可。
在zsh
:
IFS=$'\0'
du -hc -- $(locate -0 '*.doc')
或者,更好的是,使用顯式 split-on-NUL 運算子:
du -hc -- ${(0)"$(locate -0 '*.doc')"}
在bash
4.4+ 中,可能是:
readarray -td '' files < <(locate -0 '*.doc')
du -hc -- "${files[@]}"
這仍然留下兩個問題:
- 如果
locate
找不到任何文件,du -hc --
將不帶參數運行,這意味著它將為您提供當前工作目錄的磁碟使用情況。 - 另一方面,如果
locate
找到許多文件,您可能最終會達到系統呼叫的限制execve()
並獲得一個arg 列表太長錯誤。
透過執行以下操作可以避免這兩個問題:
locate -0 '*.doc' | xargs -r0 du -hc --
(-r
並且-0
是來自 GNU 實現的非標準擴展,xargs
就像-h
是來自 GNU 實現的非標準擴展du
)。
然而,這引入了一個新問題:xargs
將運行du
多次來解決execve()
限制,但這意味著您將得到幾total
行,並且多次硬連結的檔案的磁碟使用情況可能最終會被計算多次。
感謝-r
,如果找不到任何文件,則du
不會運行,但這也意味著您不會得到一行。locate
0 total
使用最新版本的 GNU du
,可以透過以下方式解決這些問題:
locate -0 '*.doc' | du --files0-from=- -hc
這次,文件列表是透過管道而不是參數傳遞的,因此參數的大小沒有限制。這也意味著不需要分配大量記憶體來儲存該列表,du
一旦locate
開始輸出它就可以開始處理它。