简洁的方式将所有行打印到与给定模式匹配的最后一行

时间:2012-01-22 16:33:27

标签: linux perl shell tail

我正试图找到一个简洁的贝壳单线,它会给我所有的 文件中的行直到某种模式。

用例是将所有行转储到日志文件中,直到找到一些为止 表示服务器已重新启动的标记。

以下是一种愚蠢的shell方式:

tail_file_to_pattern() {
    pattern=$1
    file=$2

    tail -n$((1 + $(wc -l $file | cut -d' ' -f1) - $(grep -E -n "$pattern" $file | tail -n 1 | cut -d ':' -f1))) $file
}

在stdin上获取文件的稍微更可靠的Perl方式:

perl -we '
    push @lines => $_ while <STDIN>;
    my $pattern = $ARGV[0];
    END {
        my $last_match = 0;
        for (my $i = @lines; $i--;) {
            $last_match = $i and last if $lines[$i] =~ /$pattern/;
        }
        print @lines[$last_match..$#lines];
    }
'

当然,您可以更有效地打开文件, 寻求最终并寻找回来,直到找到匹配的行。

first 出现时很容易打印所有内容,例如:

sed -n '/PATTERN/,$p'

但我没有想出一种方法来打印 last 的所有内容 次数。

7 个答案:

答案 0 :(得分:6)

这是一个仅限sed的解决方案。要打印$file 中的每一行,以$pattern匹配的最后一行开头:

sed -e "H;/${pattern}/h" -e '$g;$!d' $file

请注意,与您的示例一样,只有在文件包含模式时才能正常工作。否则,它输出整个文件。

以下是括号中的sed命令的详细信息:

  • [H]将每一行附加到sed的“保留空间”,但不要将其回显到stdout [d]。
  • 当我们遇到模式时,[h]会丢弃保留空间并从匹配线重新开始。
  • 当我们到达文件末尾时,将保留空间复制到模式空间[g],以便它回显到标准输出。

另请注意,对于非常大的文件,它可能会变慢,因为任何单遍解决方案都需要在内存中保留一堆行。

答案 1 :(得分:4)

或者:tac "$file" | sed -n '/PATTERN/,$p' | tac

编辑:如果您没有tac通过定义

来模仿它
tac() {
    cat -n | sort -nr | cut -f2
}

丑陋而POSIX。

答案 2 :(得分:4)

逐行将数据加载到数组中,并在找到模式匹配时抛出数组。打印出最后剩下的东西。

 while (<>) {
     @x=() if /$pattern/;
     push @x, $_;
 }
 print @x;

作为一个单行:

 perl -ne '@x=() if /$pattern/;push @x,$_;END{print @x}' input-file

答案 3 :(得分:3)

我建议简化shell脚本:

tail -n +$(grep -En "$pattern" "$file" | tail -1 | cut -d: -f1) "$file"

它更加简洁,因为它:

  • 使用尾部的+选项从给定的行打印到结尾,而不必计算从那里到结尾的距离。
  • 使用更简洁的方式表达命令行选项。

它通过引用$ file修复了一个错误(因此它适用于名称中包含空格的文件)。

答案 4 :(得分:3)

Sed的q命令可以解决问题:

sed "/$pattern/q" $file

这将打印所有线条,直到它到达带有图案的线条。之后,sed将打印最后一行并退出。

答案 5 :(得分:1)

此问题的标题和说明不符。

对于问题的标题,@ David W.的答案为+1。也:

sed -ne '1,/PATTERN/p'

对于身体中的问题,你已经有了一些解决方案。

请注意,tac可能特定于Linux。它似乎不存在于BSD或OSX中。如果您想要一个多平台的解决方案,请不要依赖于tac。

当然,几乎所有解决方案都要求您的数据在内存中假脱机,或者提交一次进行分析,第二次进行处理。例如:

#!/usr/local/bin/bash

tmpfile="/tmp/`basename $0`,$$"
trap "rm $tmpfile" 0 1 2 5
cat > $tmpfile

n=`awk '/PATTERN/{n=NR}END{print NR-n+1}' $tmpfile`

tail -$n $tmpfile

请注意,我对tail的使用是针对FreeBSD的。如果您使用Linux,则可能需要tail -n $n $tmpfile

答案 6 :(得分:1)

Rob Davis向我指出你想要的 并不是你真正要求的:

你说:

  

我试图找到一个简洁的shell单行程序,它会在 文件中提供所有行,直到 某些模式。< / p>

但是在帖子的最后,你说:

  

但我还没有找到一种方法来打印 上次出现的所有内容。

我已经为您提供了first question的答案。这是第二个问题的一行答案:从正则表达式打印到文件末尾:

awk '{ if ($0 ~ /'"$pattern"'/) { flag = 1 } if (flag == 1) { print $0 } }' $file

一个类似的Perl单行:

export pattern="<regex>"
export file="<file>"
perl -ne '$flag=1 if /$ENV{pattern}/;print if $flag;' $file