Shell xargs在换行符而不是空格处拆分

Shell xargs在换行符而不是空格处拆分,shell,unix,xargs,Shell,Unix,Xargs,简而言之,这就是我的问题 $ echo 'for i in $@; do echo arg: $i; done; echo DONE' > /tmp/test.sh $ echo "ac\nbc\ncc\n" | xargs bash /tmp/test.sh arg: ac arg: bc arg: cc DONE 这正是我所期望的,但是 $ echo "ac s\nbc s\ncc s\n" | xargs -d \n bash /tmp/test.sh arg: ac arg:

简而言之,这就是我的问题

$ echo 'for i in $@; do echo arg: $i; done; echo DONE' > /tmp/test.sh
$ echo "ac\nbc\ncc\n" | xargs bash /tmp/test.sh 
arg: ac
arg: bc
arg: cc
DONE
这正是我所期望的,但是

$ echo "ac s\nbc s\ncc s\n" | xargs -d \n bash /tmp/test.sh
arg: ac
arg: s
arg: bc
arg: s
arg: cc
arg: s
DONE
输出不应该是吗

arg: ac s
arg: bc s
arg: cc s
DONE
如何使用xargs获得第二个输出?

试试:

printf %b 'ac s\nbc s\ncc s\n' | xargs -d '\n' bash /tmp/test.sh
您忽略了引用传递给
-d
\n,这意味着只是
n
而不是
\n
被传递给
xargs
作为分隔符-shell“吃”了
\
(当shell解析不带引号的字符串时,
\
用作转义字符;在本例中,如果普通字符跟在
\
-
n
后面,则仅使用该普通字符)

还请注意@glenn jackman的建议,在脚本中重复引用
$@
(或者在“$@”
部分中完全省略

另外:
xargs-d
是一个GNU扩展
,例如,它在FreeBSD/macOS上不起作用。要使它在那里起作用,请参阅@glenn jackman基于
xargs-0
的解决方案


请注意,我使用的是
printf
而不是
echo
,以确保字符串中的
\n
实例在所有类似Bourne的shell中都被解释为换行:
bash
ksh
[1]中,
echo
默认不解释基于
\
的转义序列(必须使用
-e
才能实现),这与
zsh
和严格兼容POSIX的shell(如
dash

因此,
printf
是更便携的选择


[1] 根据手册,
ksh
echo
内置与主机平台的外部
echo
实用程序表现出相同的行为;虽然这可能因平台而异,但Linux和BSD/macOS实现默认情况下不会解释
\
转义序列。

您的shell脚本需要使用
“$@”
$@


我在我的机器上的xargs手册中看到:

xargs从标准输入中读取项目, 由空格[…]或换行符分隔

(强调矿山)

因此:

对于前者,你可以得到相当于

bash /tmp/test.sh ac s bc s cc s
与使用空分隔符相比

bash /tmp/test.sh "ac s" "bc s" "cc s"
当数据包含空格时,您需要清楚xargs的分隔符是什么

$ printf "%s\n" "ac s" "bc s" "cc s" | xargs -d $'\n' bash /tmp/test.sh
arg: ac s
arg: bc s
arg: cc s
DONE

$ echo $'ac s\nbc s\ncc s\n' | xargs -d $'\n' bash /tmp/test.sh  
arg: ac s
arg: bc s
arg: cc s
arg:  
DONE
注意最后一个例子中的额外参数,
echo
已经添加了一个换行符,因此您不需要额外的换行符,除非您在Mac OSX上使用
echo-n

对于参数数量已知的简单情况,告诉xargs每个命令要发送多少个参数

$ echo "1\n2\n3" | xargs -n1 echo "#"
# 1
# 2
# 3
当输入参数复杂且换行符终止时,更好的方法是:

$ echo "1\n2 3\n4 5 6" | xargs -L1 echo  "#"
# 1
# 2 3
# 4 5 6
这里有一个问题,你能看到吗?如果我们的输入行包含一个报价怎么办:

$ echo "1\n2 3\n4 '5 6" | xargs -L1 echo  "#"
# 1
# 2 3
xargs: unterminated quote
xargs
不喜欢单引号。但是
-0
-L1
不兼容,因此留给我们的是:

$ echo "1\n2 3\n4 '5 6" | tr '\n' '\0' | xargs -0 -I{} echo "#" {}
# 1
# 2 3
# 4 '5 6
如果您
brew安装findutils
,我们可以做得更好一些:

$ echo "1\n2 3\n4 '5 6" | gxargs -d\\n -i echo "#" {}
# 1
# 2 3
# 4 '5 6
但是,等等,也许使用xargs是一个不好的工具。如果我们改用shell内置程序呢:

$ echo "1\n2 3\n4 '5 6" | while read -r; do echo "# $REPLY"; done
# 1
# 2 3
# 4 '5 6

有关
xargs
vs
的更多想法,请检查此问题。

好的建议,尽管真正的问题在于没有引用传递给
-d
\n
。您完全正确:这两个问题都需要解决以解决问题(它们各自独立或组合产生症状)。您的
printf
-
\0
-
xargs-0
解决方案是一个不错的替代方案,因为它更便于携带(例如,在
xargs-d
不可用的情况下,它也适用于OSX).note-新行并不总是
\n
。对于windows,它是
\r\n
@ParamvirSinghKarwal:是的,但请注意问题已标记。以防有人在unix环境中摆弄windows样式的文件。“
xargs-0-I{}echo”#“{}
”挑剔:在
-I
之后应该有一个空格。只有不可移植的
-I
选项后面没有空格。
$ echo "1\n2 3\n4 '5 6" | while read -r; do echo "# $REPLY"; done
# 1
# 2 3
# 4 '5 6