在bash脚本中将参数回应到xargs

时间:2016-03-03 15:49:29

标签: bash

我想将一些目录传递给bash脚本,然后将这些目录传递给xargs以并行工作。不幸的是,我无法让脚本完成我想要的操作。

这是一个等同于我的脚本,我尝试使用和不使用引号:

echo $@ | xargs -i --max-procs=12 echo "do work in {}"
echo "$@" | xargs -i --max-procs=12 echo "do work in {}"

输出:

$ ./script.sh d*
do work in d1 d2 d3 d4 d5 d6 d7 d8 d9
do work in d1 d2 d3 d4 d5 d6 d7 d8 d9
$ ./script.sh "d*"
do work in d1 d2 d3 d4 d5 d6 d7 d8 d9
do work in d*

你可能已经猜到了,我想要的是这个:

$ ./script.sh d*
do work in d1
do work in d2
do work in d3
do work in d4
do work in d5
do work in d6
do work in d7
do work in d8
do work in d9

在此特定情况下,在脚本中使用find可以正常工作,但将来我可能不想使用目录。例如,如果我要传递要创建的文件列表,find就没用了。

如何让xargs将每个参数作为单独的项接受?

3 个答案:

答案 0 :(得分:2)

测试版本如下,只是为了更多地关注Jack Yates的原始问题:

printf "%s\0" "$@" | xargs -0 -I xxx --max-procs=4 printf "do work in %s\n" 'xxx'

printf不是处理位置参数的标准方法(对吗?) [编辑:Charles:printf是POSIX规定的功能,因此严格意义上符合标准,但-0的{​​{1}}参数不是;但是,自2004年修订POSIX标准以来,没有给出纯粹符合标准的机制来安全地使用xargs和任意输入。将xargs扩展到printf命令的参数列表以使每个参数重复格式字符串也符合标准;请参阅the POSIX standard for printf] 的扩展说明部分中的第9部分。

据我所知,与while循环和shift所需的进程数相比,它非常有效。根据{{​​3}},"$@"内置命令是printf的首选替代。

在以下条件下测试:

  • 超过10000个目录
  • man ascii 中找到的所有特殊和不可打印的字符已用于目录名称
  • printf,xargs和bash的手册页,测试证实了Charles Duffy的评论。

事先答复(供参考)

以下版本仅供参考。正如Charles Duffy所写,当目录名中有特殊字符时会产生错误。

echo选项指示-L 1分别处理每一行。

xargs

测试:

while [ $# -gt 0 ] ; do echo "${1}"; shift; done \
  | xargs -L 1 -I xxxx --max-procs=12 echo do work in "xxxx"

通常$ testscript.sh .??* * do work in .bashrc do work in Downloads do work in h2-1.2.139.jar do work in install do work in testscript.sh 用于解析命令行中的参数。

shift已被使用,因为根据xargs手册页不推荐使用-I xxxx

答案 1 :(得分:2)

如果要将任意参数传递给xargs,唯一安全的方法 - 正确使用所有可能的文件名,包括带有文字引号,空格或其他格式的文件名 - 是NUL分隔它们和使用xargs -0。因此:

printf '%s\0' "$@" | xargs -0 sh -c 'for f; do printf "do work in %s\n" "$@"; done'

否则,像$'hello\nworld'这样的参数 - 包含文字换行符 - 将被视为xargs中的两个参数,而不是它真正的单个参数。

答案 2 :(得分:0)

我遇到此问题的原因是-i-I选项会更改xargs期望的分隔符。也就是说,echo "$@" | xargs -n 1 do-something 按预期工作(注意:如果你有不寻常的文件名,有这种方法的注意事项;有关更多信息,请参阅其他答案),但echo "$@" | xargs -I{} -n 1 do-something {}没有。使用-I-i标志,分隔符将成为换行符。

例如:

$ cat test.sh 
echo "$@" | xargs -n 1 echo "do work in" 
echo "$@" | xargs -n 1 -I{} echo "do work in {}"
$ ls
./  ../  d1/  d2/  d3/  d4/  d5/  d6/  d7/  d8/  d9/  test.sh*
$ ./test.sh d*
do work in d1
do work in d2
do work in d3
do work in d4
do work in d5
do work in d6
do work in d7
do work in d8
do work in d9
do work in d1 d2 d3 d4 d5 d6 d7 d8 d9

来自man xargs

   -I replace-str
          Replace occurrences of replace-str in the initial-arguments with names read
          from standard input.  Also, unquoted blanks do not terminate  input  items;
          instead the separator is the newline character.  Implies -x and -L 1.

注意:根据123对原始问题的评论,-i已被弃用:

   -i[replace-str]
          This option is a synonym for -Ireplace-str if replace-str is specified, and
          for -I{} otherwise.  This option is deprecated; use -I instead.