首页 > 解决方案 > 带有正则表达式的 grep-bash 命令返回 0 个结果,正在寻找替代方案

问题描述

我正在尝试在 linux bash 中编写一些东西来导出模式以及带有有用信息的标头。例如,如果我正在阅读的源文件看起来像:

test1

test2

test3 KJHKKJKLKJJKKKKJJHJLKLKJJHHKLJHJKLHLHLHLJLKJHHKLKJHLKJHKLLJHJHKLKJJHKLLKJHJKLLKJHHKKLLKKKKKLKJHHKLLJJHHKKLLKJHJKLJJHHKLKJHJLJJKLKLJHJKKLLLJJKLLKJKLJJJLKJHJHJKKLLKJJHKLJHKLLJHHKKLKJHKKLKJJHJKLLJHHKKLLJJHJHKLJJKKHJ

test4 LKJKLLLKJHKLKLJLJKJJLKJKKHKHKHLJLHHLKLJKLHLJLKHLKJLHLHLHLKJHLKHLKHLKJLJLHLKJLJLHLJKLKHLJHJLHLHJLKJJLKHLKHLKHLKHLKJJLJLJLHLKLKJLJLLHJKHLKHLHLJHLJHLJHLJHLHLJLKJLKJLJHHJHKJHKJHKJHKKHKHKHJLJLLJLJHKHKJHKJHKJHKJHKJHJLJLJLHLJHLKLKHLKHKLKLKHKKHLKHLHH

我的数据输入图

我的模式字符串是 KKHKHKH

我要返回:test2 KJKJKJKJKLJJKJJKLLLKKJJKLJJHJKKKKHKHKH

测试4 LKJKLLLKJHKLKLJLJKJJLKJLJLHHLKLJKLHLJLKHLKJLHLHLHLKJHLKHLKHLKJLJLHLKJLJLHLJKLKHLJHJLHLHJLKJJLKHLKHLKHLKHLKJJLJLJLHLKLKJLJLLHJKHLKHLHLJHLJHLJHLJHLHLJLKJLKHKJHKJHHJ

关键是在模式处结束,而不是下面的测试。序列中也可能存在重复。我想写入每个测试标题下的最远匹配(测试 4 中的示例)

我们在课堂上学习了 grep,所以我使用 grep 和扩展正则表达式 -E 或 egrep 来尝试解决问题。我知道在一些论坛上有人尝试过 sed。

我试过用 grep -E 'test|KKHKHKH' file.txt 运行它

grep -E '>test\d+\n[LKJH]*KKHKHKH' file.txt 我再次通过调试器运行它,这次我的表达是错误的,我想我以某种方式搞砸了格式,但它之前工作过:(。只是在 bash 中不起作用

我再次期望输出是:

test2  
KJKJKJKJKLJJKJJKLLLKKJJKLJJHJKKKKHKHKH

测试4 LKJKLLLKJHKLKLJLJKJJLKJLJLHHLKLJKLHLJLKHLKJLHLHLHLKJHLKHLKHLKJLJLHLKJLJLHLJKLKHLJHJLHLHJLKJJLKHLKHLKHLKHLKJJLJLJLHLKLKJLJLLHJKHLKHLHLJHLJHLJHLJHLHLJLKJLKHKJHKJHHJ

但是对于第一个表达式 grep -E 'test|KKHKHKH' file.txt 我得到:

test1
test2
KJKJKJKJKLJJKJJKLLLKKJJKLJJHJKKKKHKHKH
test3
test4  

LKJKLLLKJHKLKLJLJKJJLKJLJLHHLKLJKLHLJLKHLKJLHLHLHLKJHLKHLKHLKJLJLHLKJLJLHLJKLKHLJHJLHLHJLKJJLKHLKHLKHLKHLKJLJLJLHLKLKJLJLLHJKHLKHLHLJHLJHLJHLJHLJLKJLKJHKJHHHJHK

我需要以某种方式只获取 test2 和 test 4 我想我可以通过 grep 再次管道它,但不知道如何获得标题。我在想我可以使用 -B 并计算前面的行数。但是每个序列可能会稍大一些。

对于最后一个表达式,我没有返回任何内容,尽管它似乎至少在某个时间点在 regex101 调试器中工作...... :(

如果没有简单的 grep 表达式,是否有我可以尝试的 bash 脚本?

标签: regexbashawksedgrep

解决方案


grep一次检查一行输入。因此,需要跨越换行符的正则表达式将永远不会匹配(没关系,\n这并不意味着 支持的正则表达式方言中的换行符grep)。你想要类似的东西

awk '/^test/ { t=$0 }
    /KKHKHKH/ { print t; print }' file.txt

如果文件总是包含test在匹配之前的上一行,你也可以说

grep -B 1 'KKHKHKH' file.txt

尽管这也会在您可能不想要的匹配之间产生一些输出。

如果匹配总是跟在我的空行之后,则对 Awk 脚本的小修改应该可以只获得每个块中的最后一个匹配:

awk '/^test/ { t=$0 }
    /KKHKHKH/ { p=$0 }
    /^$/ && p { print t; print p; t=p="" }
    END { if(p) { print t; print p }}' file.txt

END如果在最后一个块之后也肯定有空行,则该块可能是不必要的;但是在这种情况下错过最后一场比赛是一个常见的错误,并且保护措施相当简单(尽管我很懒并且产生了一些重复的代码)。


推荐阅读