Xargs 进入管道的第二侧?

Phi*_*ide 6 pipe xargs

我正在尝试执行以下操作:

cat file1.txt | xargs -I{} "cat file2.txt | grep {}"
Run Code Online (Sandbox Code Playgroud)

我期望 file1 中的每一行都是第三个管道末尾的 grep 值。它没有按预期工作。

这是因为-I{}一旦碰到管道就停止寻找要更换的东西吗?有没有解决的办法?

Sté*_*las 15

这是因为您需要一个 shell 来创建管道或执行重定向。请注意,这cat是连接命令,仅将其用于一个文件是没有意义的。

cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
Run Code Online (Sandbox Code Playgroud)

千万不能做的:

cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e {}'

因为这相当于一个命令注入漏洞。该{}会在代码参数扩展到sh如此解释为shell代码。例如,如果其中一行file1.txt$(reboot)将调用reboot.

-e(或者你也可以使用--)也很重要。没有它,您会遇到以-.

您可以使用重定向来简化上述操作,而不是cat

< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}
Run Code Online (Sandbox Code Playgroud)

或者简单地将文件名作为参数传递给grep而不是使用重定向,在这种情况下,您甚至可以删除sh

< file1.txt xargs -I{} grep -e {} file2.txt
Run Code Online (Sandbox Code Playgroud)

您还可以告诉grep在一次调用中一次查找所有正则表达式:

grep -f file1.txt file2.txt
Run Code Online (Sandbox Code Playgroud)

但是请注意,在这种情况下,对于 的每一行,这只是一个正则表达式file1.txt,没有由xargs.

xargs默认情况下,将其输入视为空白列表(在某些实现中只有空格和制表符,在其他实现[:blank:]中是当前语言环境的字符类中的任何字符)或换行符分隔的单词,反斜杠和单引号和双引号可用于转义分隔符(换行符只能通过反斜杠转义)或彼此。

例如,在如下输入上:

 'a "b'\" "bar baz" x\
y
Run Code Online (Sandbox Code Playgroud)

xargswithout-I{}将通过a "b",bar bazx<newline>y命令。

使用-I{},xargs每行获取一个单词,但仍会进行一些额外的处理。它忽略前导(但不是尾随)空格。空格不再被视为分隔符,但报价处理仍在进行中。

在上面的输入中xargs -I{}会将一个a "b" foo bar x<newline>y参数传递给命令。另请注意,根据 POSIX 的要求,如果单词长度超过 255 个字符,则许多系统将不起作用。总而言之,xargs -I{}很没用。

如果您希望每一行都作为参数逐字传递给命令,您可以使用 GNUxargs -d '\n'扩展:

< file1.txt xargs -d '\n' -n 1 grep file2.txt -e
Run Code Online (Sandbox Code Playgroud)

(这里依赖于 GNU 的另一个扩展,grep它允许在参数之后传递选项(假设环境中没有 POSIXly 正确)或可移植:

sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
  for line do
    grep -e "$line" file2.txt
  done' sh
Run Code Online (Sandbox Code Playgroud)

如果你想在每一file1.txt,而不是每个(行情仍识别)线被寻找(这也将解决您的尾随空间的问题,如果你有每行反正一个字),你可以使用xargs -n1,而不是单独使用的-I

< file1.txt xargs -n1 sh -c '
  for word do
    grep -e "$word" file2.txt
  done' sh
Run Code Online (Sandbox Code Playgroud)

要去除前导和尾随空白(但没有引用处理xargs),您还可以执行以下操作:

unset IFS # restore word splitting to its default
while read -r regexp; do
  grep -e "$regexp" file2.txt
done < file1.txt
Run Code Online (Sandbox Code Playgroud)


Kev*_*vin 8

根据您尝试执行的操作,您最好xargs完全跳过,而改用此解决方案:

grep -f file1.txt file2.txt
Run Code Online (Sandbox Code Playgroud)

这与您的原始命令不同(一旦我们按照 Stéphane Chazelas 的回答修复它)如下:

  • 行按它们出现的顺序打印,file2.txt而不管它们匹配哪种模式。在您的命令中,将打印与第一个模式匹配的所有行,然后打印与第二个模式匹配的所有行,依此类推。
  • 匹配多个模式的行只打印一次。在您的命令中,它们会为它们匹配的每个模式打印一次。
  • 可以更轻松地使用多个标志,包括-v-c

-f标志由 POSIX 指定,因此具有合理的可移植性。