如何使用grep命令搜索多个关键词并按照搜索顺序输出结果?

4
我尝试了几件事情,但似乎没有取得任何进展 - 我有一个包含一些数据行的文本文件,并且我想要从那个文件中获取一些数据行。每行都有一个唯一的标识符,我可以使用grep命令进行匹配。
如果我使用
grep 'name1\|name2\|name3\|name4' file.txt > newfile.txt

它可以完成任务并获取我想要的目标行,但是,我希望按照我指定的顺序排列这些行 - 从这个例子中,我想先有name1行,然后是name2行,然后是name3行,最后是name4行。
但是,例如在我的原始文件中,行的顺序是name2,然后是name4,然后是name3,然后是name1,输出文件似乎也按照这个顺序排列了。
是否有一种简单的方法来排序grep?
id是块排序的,因此所有具有相同名称的行都相邻出现。
感谢任何建议!

3
很抱歉,grep 不支持这个需求。你可能需要遍历单词并逐个使用 grep 进行匹配。 - fedorqui
5个回答

7

使用循环从文件中读取单词

假设有一个包含需要在其中进行grep的单词的文件,例如以下内容:

root
lp
syslog
nobody

您可以使用读取循环来反复在另一个文件中grep固定字符串。例如,使用Bash shell的默认REPLY变量和存储在/tmp目录中的单词文件,这将起作用:

while read; do
    grep --fixed-strings "$REPLY" /etc/passwd
done < /tmp/words

注意事项

  1. 发布的示例不能防止多个匹配,但是它确保匹配是按照 /tmp/words 中定义的顺序进行的。
  2. 该示例使用GNU grep和固定字符串以提高性能。在使用其他grep和正则表达式时,效果可能会有所不同。

1
您可以使用 Awk 数组。
awk 'BEGIN { k[1]="name1"; k[2]="name2"; k[3]="name3" }
{ for (i=1; i<4; ++i) if ($0 ~ k[i]) m[i]=(m[i]?m[i] RS:"") $0 }
END { for(i=1; i<4; ++i) if (m[i]) print m[i] }' file

这会产生重复,如果一行匹配多个表达式。如果需要快速优化它,只需提出要求。
或者在Perl中:
perl -ne 'BEGIN { @k = qw( name1 name2 name3 name4 );
    $k = join("", "(", join("|", @k), ")");
    $r = qr($k); }
  if(m/$r/) { push @{$m{$1}}, $_ }
  END { for $i (@k) { if ($m{$i}) {
    print join("", @{$m{$i}}); } } }' file

这可能比等效的Awk脚本更高效。它每行只会找到一个匹配项,因此并非完全等效。

...但在外观上,Awk显然更漂亮。Awk中缺乏嵌套数组有点令人遗憾,但是在Perl中将backref用作数组键的能力是我编写第二个版本的真正原因。我想这也可以在Awk中完成,尽管不太优雅。 - tripleee
谢谢您的建议!我真的希望有更简单的方法 - 我会调查这些建议! - user1637359

0
一个不太好的解决方案可能是...多次运行grep命令(也许你可以将它粘贴到一个shell脚本中并多次运行它)。
grep 'name1' file.txt > newfile.txt
grep 'name2' file.txt >> newfile.txt
grep 'name3' file.txt >> newfile.txt
grep 'name4' file.txt >> newfile.txt

希望这能帮到你!

1
一个简单的改进方法是 for word in "name1" "name2" "name3" "name4"; do grep "$word" file.txt; done > newfile.txt - Jonathan Leffler

0

列表项

我曾经在寻找同样的命令,但现在我已经弄清楚了。

步骤1:为您的grep搜索创建文件。只需将grep单词粘贴到文件中,每行一个grep关键字。例如,我制作了文件mySearch.txt。

more mySearch.txt

name2
name3
name1
name4
name2
name1

步骤2:现在使用此命令
grep -Fwf mySearch.txt file.txt>newfile.txt

cat file.txt|grep -Fwf mySearch.txt >newfile.txt

这个命令将打印所有带有name2的行,然后是name3等,最后是newfile.txt中的name1行。


-1

Perl对于这种搜索和打印会更好。

perl -lne '/name1/?push @a,$_:
          (/name2/?push @b,$_:
          (/name3/?push @c,$_:
           /name4/?push @d,$_:next));
          END{print join "\n",@a,@b,@c,@d}' your_file

以下是测试:

> cat temp
1 name1
2 name2
3 name3
4 name1
5 name4
6 name2
7 name1
> perl -lne '/name1/?push @a,$_:(/name2/?push @b,$_:(/name3/?push @c,$_:/name4/?push @d,$_:next));END{print join "\n",@a,@b,@c,@d}' temp
1 name1
4 name1
7 name1
2 name2
6 name2
3 name3
5 name4
> 

不错的问题 :)


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