观察:
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 看到输入包含两个片段:a
和b
。按照指示,xargs 对每个片段调用 bash:首先执行echo a 1
,然后执行echo b1
。
如何正确做事
find
或的某些版本xargs
允许您嵌入{}
shell 片段。这几乎总是一个坏主意,它会破坏某些文件名或其他数据,并且通常是一个安全漏洞。将数据作为单独的参数传递。
答案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"
将产生项目a
和b c
。
或者,您可以使用-d
withtr
来预处理输入,并将要用作分隔符的所有字符折叠为一个字符:
$ 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 代码片段中,因为这是不安全的,并且不可能使用任意值正确工作。