![如何将 -I{} 与 -n 大于 1 的 xargs' 一起使用?](https://linux22.com/image/153332/%E5%A6%82%E4%BD%95%E5%B0%86%20-I%7B%7D%20%E4%B8%8E%20-n%20%E5%A4%A7%E4%BA%8E%201%20%E7%9A%84%20xargs'%20%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8%EF%BC%9F.png)
我有大量文件(数以万计),需要grep
针对特定字符串进行处理。一小部分文件中有空格。文件如此之多,进程创建的开销-n1
实际上比文件搜索本身还要大。
这可行,但速度慢得无法使用:
cat filelist | xargs -I{} grep mystring '{}'
因此,我想向每个grep
实例传递 1000 个参数,如下所示:
cat filelist | xargs -n1000 -I{} grep mystring '{}'
但这不起作用。似乎只有当= 1{}
时才有效?!-n
例子:
进程太多,正确输出: $ 序列 1 10 | xargs -I{} -n1 echo "<{}>" <1> <2> <3> <4> <5> <6> <7> <8> <9> <10>
进程数量很多,然后...什么? $ 序列 1 10 | xargs -I{} -n2 echo "<{}>" <{}> 1 2 <{}> 3 4 <{}> 5 6 <{}> 7 8 <{}> 9 10
也许我可以用它find
来代替。
答案1
是的,-I
一次仅适用于一个参数。使用 时-I
,输入也会以与不使用时不同的方式解析为参数(使用-n
或不使用)。
每个非空行都会有-I{}
一个单词(除了仍然可以通过用反斜杠引用它来嵌入换行符),前导空白字符但不是尾随空白字符(其列表因某些实现和区域设置而异)已删除。引号("
,'
和仍然以与s\
不同的方式进行处理)。sh
如果没有-I{}
,单词将以空格(至少 SPC、TAB 和 NL)分隔,并处理引号。
比较:
$ printf ' a "b c" \n' | xargs -n1 printf '<%s>\n'
<a>
<b c>
$ printf ' a "b c" \n' | xargs -I{} printf '<%s>\n' {}
<a b c >
IMO,xargs
有点混乱,唯一可靠/有用的使用方法是使用-0
和-d
GNU 扩展。
如果您想一次运行带有多个参数的命令并为每个参数使用不同的占位符,最好是使用sh
:
xargs < filelist -r -n2 sh -c 'printf "1: %s\n2: %s\n" "$1" "$2"' sh
在这里,xargs
一次将 2 个参数传递给sh
,并用和sh
进行占位(另请参阅一次传递所有参数)。"$1"
"$2"
"$@"
这是默认的单词标记化xargs
。如果filelist
每行包含一个文件,您可以使用 GNUxargs
的-d '\n'
.
对于您的grep
示例,您不需要-n
也不需要-I
,只需:
xargs < filelist grep mystring
然后xargs
将传递尽可能多的参数grep
(参数添加在最后)。我们可以没有-r
这里(GNU 扩展),就好像filelist
全是空白一样,仍然grep
在没有文件参数的情况下运行(这-r
会阻止)应该是无害的,因为它会在filelist
.
但是,您可能想使用-H
GNU 的选项grep
,或将其运行为:
xargs < filelist grep mystring /dev/null
确保grep
在找到匹配项时始终打印文件名,即使filelist
只包含一个单词。
答案2
这可以工作:
xargs -I '{}' -n 1 -P 1000 grep mystring '{}' < file_list.txt
- -我替换-str
- -n最大参数
- -P最大进程数
答案3
我无法发表评论,但我觉得这可能与其他需要具有-I{}
指定数量 args 的功能的人相关-n
。
就我而言,我想一次只将一个参数传递给目标命令,但是不是作为位置参数。我需要将输入 arg 作为目标命令传递给选项,选项后面包含目标命令的更多样板组件。
例子:target-command -opt="input-arg-1" -x -y some/path
因此纯 xargs 不起作用,因为它将输入 arg 附加到命令的末尾。并且xargs -I{}
不会起作用,因为它会破坏输入分隔符并且一次仅适用于一个输入参数。我正在为 Darwin 和 Linux 进行开发,并且(据我所知)没有可以与这两个平台的 Bash 安装一起使用的选项组合。
@Stéphane 的答案是我解决方案的关键:我们可以将目标命令包装在子 shell 中,并使用位置参数提取我们的输入参数。这是对我有用的实际代码行:
echo $MODULES | xargs -n1 sh -c 'terraform -chdir="$1" init' --
我们让 xargs 在不使用 的情况下完成它的工作-I
,因此它可以正确解析输入。然后它将参数传递给子 shell,您可以在子 shell 中将参数插入到目标命令中。
请注意命令--
末尾的sh -c
。这告诉 sh 将任何后续参数作为常规位置参数传递到子 shell 中。我已经用 Bash 和 BusyBox (ash) 对此进行了测试,并且两者都有效。其他 shell 也有类似的功能,但我还没有探索过。
答案4
GNU Parallel 已修复此问题。 -X 将包含上下文:
$ seq 1 10 | parallel -j1 -qX echo "<{}>"
<1> <2> <3> <4> <5> <6> <7> <8> <9> <10>
-m 不会:
$ seq 1 10 | parallel -j1 -qm echo "<{}>"
<1 2 3 4 5 6 7 8 9 10>