xargs多个参数

85

我有一个源文件输入,input.txt

a.txt
b.txt
c.txt

我想把这些输入作为以下方式提供给程序:

my-program --file=a.txt --file=b.txt --file=c.txt

我尝试使用 xargs,但没有成功。

cat input.txt | xargs -i echo "my-program --file"{}

它给出了

my-program --file=a.txt
my-program --file=b.txt
my-program --file=c.txt

但是我想要

my-program --file=a.txt --file=b.txt --file=c.txt

有任何想法吗?


当我尝试按年份对文件系统进行分层时,seq 1996 2022 | xargs 调用非常有用。 - Sridhar Sarnobat
17个回答

180

不要听他们的。 :) 只看这个例子:

echo argument1 argument2 argument3 | xargs -l bash -c 'echo this is first:$0 second:$1 third:$2'

输出结果将是:

this is first:argument1 second:argument2 third:argument3

29
“不要听他们的话”让我笑了。 - user3751385
20
如果您想在OSX上执行此操作,请使用-L1,例如:echo argument1 argument2 argument3 | xargs -L1 bash -c 'echo this is first:$0 second:$1 third:$2' | xargs - patrickdavey
4
我认为这个答案是错误的。文件有三行,但在这个回答中却用了一行表示三个参数! - Mohsen Abasi
11
为什么需要在结尾使用第二次管道到xargs? - np20
1
这真是太美妙了。 - RicHincapie
显示剩余7条评论

41

到目前为止,所提供的解决方案都不能正确处理包含空格的文件名。一些甚至会在文件名包含 ' 或 " 时失败。如果您的输入文件是由用户生成的,则应准备好面对意外的文件名。

GNU Parallel 可以很好地处理这些文件名,并为您提供(至少)3种不同的解决方案。如果您的程序需要3个参数且仅需要3个参数,则此方法可行:

(echo a1.txt; echo b1.txt; echo c1.txt;
 echo a2.txt; echo b2.txt; echo c2.txt;) |
parallel -N 3 my-program --file={1} --file={2} --file={3}

或者:

(echo a1.txt; echo b1.txt; echo c1.txt;
 echo a2.txt; echo b2.txt; echo c2.txt;) |
parallel -X -N 3 my-program --file={}

但是,如果您的程序接受的参数数量与命令行上可用空间一样多:

(echo a1.txt; echo b1.txt; echo c1.txt;
 echo d1.txt; echo e1.txt; echo f1.txt;) |
parallel -X my-program --file={}

观看介绍视频以了解更多信息:http://www.youtube.com/watch?v=OpaiGYxkSuQ


2
有趣 - 不知道 GNU Parallel。Bash 是一个理智的 shell;你不需要在管道后面加反斜杠告诉它下一行命令。 - Jonathan Leffler
1
GNU parallel非常好用。但是需要注意的是,在某些系统上,你需要给parallel加上"--gnu"选项才能使其正常工作。我不再使用xargs了。如果你真的不想并行运行,你总可以给它加上"-j 1"选项(1个作业)。 - travc
2
工具parallel并不总是可用的,特别是在普通的Unix系统中,如果可能的话需要安装。下面有一个有趣的解决方案,使用标准的Unix工具。 - oklas
另一种更灵活的标准工具解决方案 - Moshe Bixenshpaner

23
如何尝试这样做:
echo $'a.txt\nb.txt\nc.txt' | xargs -n 3 sh -c '
   echo my-program --file="$1" --file="$2" --file="$3"
' argv0

12

如果您使用两个xargs调用,那将更加简单: 第一个调用将每一行转换为--file=...,第二个调用执行xargs操作 ->

$ cat input.txt | xargs -I@ echo --file=@ | xargs echo my-program
my-program --file=a.txt --file=b.txt --file=c.txt

7

您可以使用sed在每行前加上--file=,然后调用xargs

sed -e 's/^/--file=/' input.txt | xargs my-program

1
在谷歌上搜索了一下(四年后),我想出了自己的(类似的)解决方案...特别是对于这个问题,更像是: echo a.txt b.txt c.txt | xargs | sed 's/ / --file=/g' | xargs echo my-program --file 我的复杂解决方案更多地是将文件名(例如Apache日志)输入到一个长命令行中。例如: ls /var/log/apache2/*access.log | xargs | sed 's/ / -f /g' | xargs echo myprogram -f - RVT

5

这里有一个使用sed的解决方案,可以处理三个参数,但它的局限性在于对每个参数应用相同的转换:

cat input.txt | sed 's/^/--file=/g' | xargs -n3 my-program

这里有一种方法可以适用于两个参数,但也允许更大的灵活性:

cat input.txt | xargs -n 2 | xargs -I{} sh -c 'V="{}"; my-program -file=${V% *} -file=${V#* }'

5
我发现一个类似的问题,并找到了一种比目前为止介绍的方法更好、更干净的解决方案。
对于您的示例,我使用的 xargs 语法如下:
xargs -I X echo --file=X

完整的命令行为:

my-program $(cat input.txt | xargs -I X echo --file=X)

这将像是一个

工作。
my-program --file=a.txt --file=b.txt --file=c.txt

已完成(假设input.txt包含来自您的示例的数据)。


事实上,在我的情况下,我需要先找到文件并对它们进行排序,所以我的命令行看起来像这样:

my-program $(find base/path -name "some*pattern" -print0 | sort -z | xargs -0 -I X echo --files=X)

可能不太清楚的一些细节(对我来说也是):
  • some*pattern 必须加引号,否则 shell 会在传递给 find 前展开它。
  • -print0-z 和最后的 -0 使用空值分隔符以确保正确处理具有空格或其他奇怪名称的文件。

不过需要注意的是,我还没有深入测试它。尽管它似乎能工作。


谢谢!使用$(...)来内联执行命令更加简洁! - Didi Bear
@DidiBear永远不要使用some_command $(...),它会受到通配符扩展和单词分割的影响;看看这个例子:printf '%s\n' $(echo 'filename with * .txt'),它输出的是filename with * .txt吗? - Fravadona

2

xargs不能这样使用。尝试使用以下方法:

  myprogram $(sed -e 's/^/--file=/' input.txt)

1

我正在寻找解决这个确切问题的方法,并得出了编写一个中间脚本的结论。

要使用 -n '\n' 分隔符转换下一个示例的标准输出

例如:

 user@mybox:~$ echo "file1.txt file2.txt" | xargs -n1 ScriptInTheMiddle.sh

 inside the ScriptInTheMidle.sh:
 !#/bin/bash
 var1=`echo $1 | cut -d ' ' -f1 `
 var2=`echo $1 | cut -d ' ' -f2 `
 myprogram  "--file1="$var1 "--file2="$var2 

为使该解决方案生效,需要在那些参数file1.txt和file2.txt之间添加空格,或选择任意分隔符,另外,在脚本中,请确保检查-f1和-f2的含义是“取第一个单词”和“取第二个单词”,具体取决于找到的第一个分隔符的位置(分隔符可以是' ' ';' '.' 或您希望使用的任何其他字符)。

通过xargs、cut和一些bash脚本编写,问题得到了解决。

祝好!

如果您愿意顺便看看,我有一些有用的提示http://hongouru.blogspot.com


1

这是因为echo会打印一个新行。尝试使用以下代码:

echo my-program `xargs --arg-file input.txt -i echo -n " --file "{}`

1
当输入达到30,000个文件名时会发生什么? - Jonathan Leffler
1
使用子shell扩展,例如 echo my-program $(xargs --arg-file input.txt -i echo -n " --file "{}) - thiagowfx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接