使用“grep -f file”命令按文件顺序打印文件内容

9

我有一个需求,需要从文件中grep出匹配的模式,但必须按顺序。

$ cat patt.grep
name1
name2

$ grep -f patt.grep myfile.log
name2:some xxxxxxxxxx
name1:some xxxxxxxxxx

我得到的输出是先找到name2并打印,然后找到name1并打印。但是我的要求是按照patt.grep文件的顺序首先获取name1。

我希望的输出是:

name1:some xxxxxxxxxx
name2:some xxxxxxxxxx

1
@devnull 可能 patt.grep 中的模式是未排序的,而 OP 希望按照 patt.grep 中的顺序进行排序。不幸的是,这个例子可能会引起误导。 - mockinterface
@JKB 我敢打赌它是未排序的。这是我多年阅读需求编写者思维后形成的直觉。我会睡觉,明天早上再看看 :) - mockinterface
@Sriharsha Kalluru 请尝试 $ grep -f patt.grep myfile.log | sort -u - Jayesh Bhoi
sort -u 适用于我的脚本,但我的要求是按照 patt.grep 文件列表的顺序获取排序。 - Sriharsha Kalluru
2
该命令的输出顺序与 patt.grep 中模式的顺序无关,而是按照 myfile.log 中行出现的顺序排列。在 name1 之前,name2 就已经出现在 myfile.log 中了。grep 按行遍历待搜索的文件,并将每一行与所有模式进行比较。如果您想按模式顺序排序,则需要多次运行 grep,每次针对一个模式运行一次。 - twalberg
显示剩余2条评论
6个回答

4
你可以通过将patt.grep导入到xargs中,逐个传递模式给grep
默认情况下,xargs会将参数附加到命令的末尾。但在这种情况下,grep需要myfile.log作为最后一个参数。所以使用-I{}选项告诉xargs用参数替换{}
cat patt.grep | xargs -Ihello grep hello myfile.log

如果在性能方面可以容忍为patt.grep中的每一行调用grep,那么这是一个简单而实用的解决方案。(我建议使用{}或类似抽象的占位符,以避免混淆。) - mklement0

1

按照出现顺序逐行阅读,使用patt.grep中的正则表达式:

while read ptn; do grep $ptn myfile.log; done < patt.grep

2
while循环将创建n个进程,如果有n个模式,则我不喜欢那种方式。我正在寻找通用选项,如果grep直接提供该选项的话。 - Sriharsha Kalluru
也许可以对“grep -f patt.grep myfile.log”的输出应用逐行匹配的方法? - J. Katzwinkel
我不知道逐行匹配,请提供命令。 - Sriharsha Kalluru

1
一个简单的解决方法是在使用grep之前对日志文件进行sort排序:
grep -f patt.grep <(sort -t: myfile.log)

然而,如果 patt.grep 没有排序,则可能无法按期望顺序产生结果。
为了保留模式文件中指定的顺序,您可以改用 awk
awk -F: 'NR==FNR{a[$0];next}$1 in a' patt.grep myfile.log

但我认为 OP 希望按照 patt.grep 文件列表的顺序排序。 - Jayesh Bhoi
1
@JKB 是的,awk 解决方案确实保留了模式文件中的顺序。 - devnull
实际上,awk 解决方案保留了与 _log_(输入)文件中相同的顺序,而不是模式文件;它实际上与 OP 的原始命令 grep -f patt.grep myfile.log 相同。 - mklement0

1

这应该可以做到

awk -F":" 'NR==FNR{a[$1]=$0;next}{ if ($1 in a) {print a[$0]} else {print $1, $1} }' myfile.log patt.grep > z


虽然这段代码片段可能回答了问题,但我们通常更喜欢一些额外的解释来说明为什么或如何这样做。你能提供一个解释吗? - Joshua Drake
抱歉,首先,使用“-F”:“”将myfile.log拆分为列。然后我将内容加载到a[$1]=$0中。然后我说,如果patt.grep的第一列(也是唯一的列)中列出的单词存在于a的第一列中(它基本上是通过使用“-F”:“”进行拆分并按顺序包含name2,name1),那么我就打印整行,否则打印缺少的单词两次。因此,如果您将name3添加到patt.grep中,则输出为: name1:一些xxxxxxxxxx name2:一些xxxxxxxxxx name3 name3 - Isidor Lipsch

1

我尝试了相同的情况,很容易使用以下命令解决:

我认为如果您的数据与您所表示的格式相同,则可以使用此方法。

grep -f patt.grep myfile.log | sort

enter image description here


1
但我认为 OP 希望按照 patt.grep 文件列表的顺序排序。 - Jayesh Bhoi
我提到了name1和name2只是为了测试,但我的实际需求可能是用不同的词。 - Sriharsha Kalluru
然后您可以使用“while read line; do grep $line myfile.log; done < patt.grep”,我认为答案已经由J. Katzwinkel在下面给出了。 - Tajinder

0

这不能仅通过 grep 完成。

对于一个简单而实用但效率低下的解决方案,请参见 owlman's answer。它会为 patt.grep 中的每个模式调用一次 grep

如果那不是一个选项,请考虑以下方法:

grep -f patt.grep myfile.log |
 awk -F: 'NR==FNR { l[$1]=$0; next } $1 in l {print l[$1]}' - patt.grep
  • 将所有模式一次性传递给grep
  • 然后使用awk根据patt.grep中模式的顺序进行排序:
    • 首先使用第一个基于:的字段作为键将所有输出行(通过stdin,-即通过管道传递)读入关联数组中
    • 然后循环遍历patt.grep的行,并打印相应的输出行(如果有的话)。

限制条件

  • 假设patt.grep中的所有模式都与日志文件中的第一个基于:的标记匹配,正如问题中的示例输出数据所暗示的那样。
  • 假设每个模式只匹配一次 - 如果可能存在多个匹配,则必须使awk解决方案更加复杂。

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