使用xargs从管道Grep N次

时间:2016-01-16 05:26:03

标签: bash unix awk grep xargs

我有一个名为input的文件,其中包含维基百科或维基百科标题的子串列表。我只想打印出维基百科标题的行,而不是子串。

我有另一个名为wikititle的文件,其中包含所有维基百科标题的列表。所以我想从输入中grep每一行,如果它与^ {string} $匹配,我想打印出那一行。

我想出了以下命令:

cat input | xargs -0 -I{} bash -c 'grep -q -w ^{}$ wikititle && { echo {}; }'

但它给了我一个错误:

 xargs: command too long

我该如何实现?谢谢!

2 个答案:

答案 0 :(得分:3)

打印两个文件中找到的行的正确方法是使用comm

comm -12 <(sort input) <(sort wikititle)

这比你试图做的更强大 :它只运行一次,并且需要一次只在内存中存储很少的内容(sort可以有更大的内存需求,但GNU实现支持使用磁盘支持的临时存储。)

另一种更有效的方法如下:

grep -F -x -f input wikititle

...这只会运行grep 一次,使用input中给出的所有(换行符分隔的)字符串对wikititle的内容

使用grep -F避免将参数视为正则表达式,因此即使像Foo [Bar]这样的字符串在完全锚定时也会匹配它们(因为它们不会使用将[Bar]视为字符的grep类)。使用-x需要完整的匹配(谢谢,@ tripleee!)。

...并且,如果您真的想要使用xargs和一大堆单独的grep调用以及shell级别echo有充分的理由......

<input xargs bash -c \
  'for line; do grep -q -F -x -e "$line" wikititle && printf '%s\n' "$line"; done' _

请注意,这不使用-I '{}',这是一个使xargs效率低得多的选项(强制它为每次匹配运行一次命令),并且还会引入潜在的安全漏洞当与bash -c一起使用时(如果输入文件中的一行包含$(rm -rf ~),您可能不想执行它)。相反,它在你的bash中使用for循环来迭代作为参数传递的文件名。

答案 1 :(得分:1)

没有样本输入和预期输出,这是一个猜测,但听起来就像你需要的只是:

awk 'NR==FNR{titles[$0];next} $0 in titles' wikititle input

请记住,shell是一个操作文件和进程并调用工具的环境,而不是操作文本的工具。创建shell的人也为shell创建了awk来调用操作文本。