正确转义 xargs 中管道的输出

Ger*_*nsa 7 ls shell xargs quoting

例子:

% touch -- safe-name -name-with-dash-prefix "name with space" \
    'name-with-double-quote"' "name-with-single-quote'" \
    'name-with-backslash\'
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

如果我们使用该-0选项,它会遇到带有破折号前缀的名称的问题:

% ls -- * | xargs -0 -- ls -l --
ls: invalid option -- 'e'
Try 'ls --help' for more information.
Run Code Online (Sandbox Code Playgroud)

这是在使用其他可能有问题的字符(如换行符、控制字符等)之前。

Sté*_*las 8

POSIX规范确实给你一个例子:

ls | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' | xargs -E '' printf '<%s>\n'
Run Code Online (Sandbox Code Playgroud)

(文件名是任意字节序列(除了/和 NULL)和sed/xargs期望text,您还需要将语言环境修复为 C(其中所有非 NUL 字节都将生成有效字符)以使其可靠(xargs实现除外)对参数的最大长度有非常低的限制))

-E ''是需要一些xargs实现方式中,如果没有它,将理解一个_参数来表示输入结束(其中echo a _ b | xargs输出a仅例如)。

使用 GNU xargs,您可以使用:

ls | xargs -d '\n' printf '<%s>\n'
Run Code Online (Sandbox Code Playgroud)

GNUxargs也有一个-0被其他一些实现复制的,所以:

ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'
Run Code Online (Sandbox Code Playgroud)

稍微便携一些。

所有这些都假定文件名不包含换行符。如果可能存在带有换行符的文件名,则 的输出ls根本无法进行后处理。如果你得到:

a
b
Run Code Online (Sandbox Code Playgroud)

这可以是两个ab文件a<newline>b,也可以是一个名为 的文件,无法分辨。

GNUls具有--quoting-style=shell-always使其输出明确且可后处理的 a ,但引用与xargs. xargs认识"..."\x'...'引用的形式。但是两者"..."和 和'...'都是强引号并且不能包含换行符(只能为\转义换行符xargs),所以这与 sh 引用不兼容,其中只有'...'强引号(并且可以包含换行符),但它\<newline>是一个行延续(已删除) ) 而不是转义的换行符。

您可以使用 shell 来解析该输出,然后以预期的格式输出它xargs

eval "files=($(ls --quoting-style=shell-always))"
[ "${#files[#]}" -eq 0 ] || printf '%s\0' "${files[@]}" |
  xargs -0 printf '<%s>\n'
Run Code Online (Sandbox Code Playgroud)