为什么 xargs 不处理最后一个参数?

为什么 xargs 不处理最后一个参数?

观察:

mark@L-R910LPKW:~$ echo a b | xargs -d' ' -I{} bash -c 'echo {} 1'
a 1
b
bash: line 2: 1: command not found
mark@L-R910LPKW:~$

到底是怎么回事?

答案1

什么地方出了错

b出现在输出中,因此它已被处理,但不是按照您预期的方式处理。

第一步,让 bash 告诉您它看到了什么:传递-x选项以启用其跟踪。

$ echo a b | xargs -d' ' -I{} bash -x -c 'echo {} 1'
+ echo a 1
a 1
+ echo b
b
+ 1
bash: line 2: 1: command not found

echo a 1因此 bash按预期首先被调用。但下一行echo b并不echo b 1像你想象的那样。还有一个额外的行1。为什么?

好吧,你告诉 xargs 以空格分割。并且您传递了输入,a b␤其中是换行符。因此 xargs 看到输入包含两个片段:ab␤。按照指示,xargs 对每个片段调用 bash:首先执行echo a 1,然后执行echo b␤1

如何正确做事

find或的某些版本xargs允许您嵌入{}shell 片段。这几乎总是一个坏主意,它会破坏某些文件名或其他数据,并且通常是一个安全漏洞。将数据作为单独的参数传递。

是否可以安全地使用“find -exec sh -c”?

答案2

作为吉尔斯在回答中提到-d ' 'GNU xargs 的选项使其考虑空格,并且仅考虑空格作为分隔符,将换行符保留为数据的一部分,在这里像字母本身一样嵌入到您的 shell 代码中。那可能不是你想要的。 (最常见的用途可能-d-d '\n'告诉它按原样使用行,而不-L进行任何进一步的处理,例如。)

相反,如果您希望将每个空格分隔的单词作为单独的项目,则一种选择是利用默认行为,即在空格上拆分项目,因此这可以直接工作:

$ echo a b | xargs -n1 bash -c 'echo "$1" 1' sh
a 1
b 1

请注意,它还处理引号和反斜杠(与 shell 略有不同),因此这与仅使用空格作为分隔符不同。输入a "b c"将产生项目ab c

或者,您可以使用-dwithtr来预处理输入,并将要用作分隔符的所有字符折叠为一个字符:

$ printf 'a b\nc\n' | tr ' ' '\n' | xargs -d'\n' -n1 bash -c 'echo "$1" 1' sh
a 1
b 1
c 1

然而,-d这不是标准的,我认为只在 GNU xargs 中实现,所以你可能想改用-0具有更广泛支持的:

$ printf 'a b\nc\n' | tr ' \n' '\0' | xargs -0 -n1 bash -c 'echo "$1" 1' sh
a 1
b 1
c 1

无论如何,请避免将值直接嵌入到 shell 代码片段中,因为这是不安全的,并且不可能使用任意值正确工作。

相关内容