Shell

正確轉義 xargs 中管道的輸出

  • February 25, 2022

例子:

% 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 擴展)不執行)。

GNUxargs也有一個-0已被其他一些實現複製的,所以:

ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'

稍微更便攜。

所有這些都假設文件名不包含換行符。如果可能存在帶有換行符的文件名,則 的輸出ls根本不可後處理。如果你得到:

a
b

這可以是 aabfiles,也可以是一個名為a<newline>b的文件,無法判斷。

GNUls有一個--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'

引用自:https://unix.stackexchange.com/questions/385466