Xargs 进入管道的第二侧?

Xargs 进入管道的第二侧?

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

cat file1.txt | xargs -I{} "cat file2.txt | grep {}"

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

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

答案1

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

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

不是做:

猫文件1.txt | xargs -I{} sh -c 'cat file2.txt | xargs -I{} sh -c 'cat file2.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 {}

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

< file1.txt xargs -I{} grep -e {} file2.txt

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

grep -f file1.txt file2.txt

但请注意,在这种情况下, 的每一行都只有一个正则表达式file1.txt, 没有进行任何特殊的引号处理xargs

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

例如,对于这样的输入:

 'a "b'\" "bar baz" x\
y

xargs没有-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

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

sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
  for line do
    grep -e "$line" file2.txt
  done' sh

如果你想要每一个单词file1.txt(引号仍然被识别)而不是每个线要查找(如果每行只有一个单词,这也可以解决尾随空格问题),您可以xargs -n1单独使用而不是使用-I

< file1.txt xargs -n1 sh -c '
  for word do
    grep -e "$word" file2.txt
  done' sh

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

unset IFS # restore word splitting to its default
while read -r regexp; do
  grep -e "$regexp" file2.txt
done < file1.txt

答案2

根据您想要执行的操作,您最好xargs完全跳过并使用此解决方案:

grep -f file1.txt file2.txt

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

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

国旗-f由 POSIX 指定因此相当便携。

相关内容