如何使用grep匹配内容的最后一次出现而非首次
如何提取每个文件中匹配文本的最后一次出现
刚好碰到过类似的需求!你现在是想遍历ibrav8_c11mc12/下的所有文件,每个文件只保留最后一行包含!的内容,而不是把所有匹配行都列出来对吧?下面给你几个实用的解决方案:
方法1:用循环结合tac+grep实现
tac命令会把文件内容倒序输出,所以我们可以用它快速定位到原文件的最后一个匹配行:
for file in ibrav8_c11mc12/*; do # 只处理普通文件,跳过目录等非文件条目 if [ -f "$file" ]; then # 倒读文件,取第一个匹配行(即原文件的最后一个匹配),再加上文件名前缀 tac "$file" | grep -m1 "!" | sed "s/^/$file:/" fi done
这里的grep -m1表示只取第一个匹配结果,配合tac就刚好拿到原文件的最后一个匹配行,最后用sed给行内容加上文件名,和grep默认的输出格式完全一致。
方法2:用awk一次性处理,更高效
如果文件数量较多,用awk可以避免循环,直接批量处理所有文件,效率更高:
awk '/!/ { last_match = FILENAME ":" $0 } ENDFILE { print last_match }' ibrav8_c11mc12/*
原理很简单:
- 每匹配到一行带
!的内容,就把「文件名:行内容」存到last_match变量里,后续匹配会自动覆盖之前的内容 - 当每个文件处理完成时(
ENDFILE事件),打印最后一次保存的last_match,也就是该文件的最后一个匹配行
这个方法输出的格式和你原来的grep完全一致,而且不用单独处理目录,awk会自动跳过非文件的条目。
额外适配提示
如果你的系统是macOS(默认是BSD版本的awk),ENDFILE是GNU awk的特性,可以换成下面的写法,效果完全相同:
awk 'FNR == 1 { if (last_match) print last_match; last_match = "" } /!/ { last_match = FILENAME ":" $0 } END { print last_match }' ibrav8_c11mc12/*
内容的提问来源于stack exchange,提问作者Daniel Marchand
相关产品推荐
相关产品推荐

