Shell
正確轉義 xargs 中管道的輸出
例子:
% touch -- safe-name -name-with-dash-prefix "name with space" \ 'name-with-double-quote"' "name-with-single-quote'" \ 'name-with-backslash\'
xargs
似乎無法處理雙引號:% ls | xargs ls -l xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option ls: invalid option -- 'e' Try 'ls --help' for more information.
如果我們使用該
-0
選項,則帶有破折號前綴的名稱會出現問題:% ls -- * | xargs -0 -- ls -l -- ls: invalid option -- 'e' Try 'ls --help' for more information.
這是在使用其他可能有問題的字元(如換行符、控製字元等)之前。
POSIX 規範確實為您提供了一個範例:
ls | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' | xargs -E '' printf '<%s>\n'
(文件名是任意字節序列(除了
/
和 NULL)和sed
/xargs
期望text,您還需要將語言環境修復為 C (所有非 NUL 字節都將成為有效字元)以使其可靠(xargs
實現除外對參數的最大長度有非常低的限制))
-E ''
某些xargs
實現需要 ,如果沒有它,將理解表示_
輸入結束的參數(例如僅echo a _ b | xargs
輸出)。a
使用 GNU
xargs
,您可以使用:ls | xargs -rd '\n' printf '<%s>\n'
(如果輸入為空,還為命令添加
-r
(也是 GNU 擴展)不執行)。GNU
xargs
也有一個-0
已被其他一些實現複製的,所以:ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'
稍微更便攜。
所有這些都假設文件名不包含換行符。如果可能存在帶有換行符的文件名,則 的輸出
ls
根本不可後處理。如果你得到:a b
這可以是 a
a
和b
files,也可以是一個名為a<newline>b
的文件,無法判斷。GNU
ls
有一個--quoting-style=shell-always
可以使其輸出明確並且可以進行後處理,但引用與xargs
.xargs
認識"..."
和引用\x
的'...'
形式。但是"..."
and'...'
都是強引號並且不能包含換行符(只能\
為 轉義換行符xargs
),因此這與 sh 引用不兼容,其中只有'...'
強引號(並且可以包含換行符)但是\<newline>
是續行符(已刪除) 而不是轉義的換行符。您可以使用 shell 解析該輸出,然後以預期的格式輸出
xargs
:eval "files=($(ls --quoting-style=shell-always))" [ "${#files[@]}" -eq 0 ] || printf '%s\0' "${files[@]}" | xargs -0 printf '<%s>\n'
或者,您可以讓 shell 獲取文件列表並將其以 NUL 分隔傳遞給
xargs
. 例如:
- 與
zsh
:print -rNC1 -- *(N) | xargs -r0 printf '<%s>\n'
- 與
ksh93
:(set -- ~(N)*; (($# == 0)) || printf '%s\0' "$@") | xargs -r0 printf '<%s>\n'
- 與
fish
:begin set -l files *; string join0 -- $files; end | xargs -r0 printf '<%s>\n'
- 與
bash
:( shopt -s nullglob set -- * (($# == 0)) || printf '%s\0' "$@" ) | xargs -r0 printf '<%s>\n'