首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从文本文件中获取特定行

从文本文件中获取特定行
EN

Stack Overflow用户
提问于 2008-12-20 19:10:26
回答 4查看 10.2K关注 0票数 6

我在一个UNIX机器上工作,并试图运行一个应用程序,该应用程序为标准输出提供了一些调试日志。我已经将此输出重定向到一个日志文件,但现在希望获得显示错误的行。

我这里的问题是一个简单的

代码语言:javascript
复制
cat output.log | grep FAIL

也帮不上忙。因为这只显示了在它们中失败的行。我想要更多的信息伴随这一点。就像这条线上面的2-3行,失败了。有没有办法通过一个简单的shell命令来做到这一点呢?我想有一个单一的命令行(可以有管道)来做上面的事情。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2008-12-20 19:13:45

grep -A $NUM

这将在匹配后打印尾随上下文的$NUM行。

-B $NUM打印前导上下文。

man grep是你最好的朋友。

所以在你的例子中:

cat日志| grep -A 3 -B 3 FAIL

票数 5
EN

Stack Overflow用户

发布于 2008-12-20 19:14:18

代码语言:javascript
复制
grep -C 3 FAIL output.log

请注意,这也去掉了useless use of cat (UUOC)

票数 9
EN

Stack Overflow用户

发布于 2008-12-20 19:38:09

我有两种我称之为sgrep的实现,一种是用Perl实现的,一种是只使用pre-Perl (pre-GNU)标准Unix命令的。如果你有GNU grep,你就不需要这些了。处理前向和后向上下文搜索会更复杂,但这可能是一种有用的练习。

Perl解决方案:

代码语言:javascript
复制
#!/usr/perl/v5.8.8/bin/perl -w
#
# @(#)$Id: sgrep.pl,v 1.6 2007/09/18 22:55:20 jleffler Exp $
#
# Perl-based SGREP (special grep) command
#
# Print lines around the line that matches (by default, 3 before and 3 after).
# By default, include file names if more than one file to search.
#
# Options:
# -b n1     Print n1 lines before match
# -f n2     Print n2 lines following match
# -n        Print line numbers
# -h        Do not print file names
# -H        Do     print file names

use strict;
use constant debug => 0;
use Getopt::Std;
my(%opts);

sub usage
{
    print STDERR "Usage: $0 [-hnH] [-b n1] [-f n2] pattern [file ...]\n";
    exit 1;
}

usage unless getopts('hnf:b:H', \%opts);
usage unless @ARGV >= 1;

if ($opts{h} && $opts{H})
{
    print STDERR "$0: mutually exclusive options -h and -H specified\n";
    exit 1;
}

my $op = shift;

print "# regex = $op\n" if debug;

# print file names if -h omitted and more than one argument
$opts{F} = (defined $opts{H} || (!defined $opts{h} and scalar @ARGV > 1)) ? 1 : 0;
$opts{n} = 0 unless defined $opts{n};

my $before = (defined $opts{b}) ? $opts{b} + 0 : 3;
my $after  = (defined $opts{f}) ? $opts{f} + 0 : 3;

print "# before = $before; after = $after\n" if debug;

my @lines = (); # Accumulated lines
my $tail  = 0;  # Line number of last line in list
my $tbp_1 = 0;  # First line to be printed
my $tbp_2 = 0;  # Last line to be printed

# Print lines from @lines in the range $tbp_1 .. $tbp_2,
# leaving $leave lines in the array for future use.
sub print_leaving
{
    my ($leave) = @_;
    while (scalar(@lines) > $leave)
    {
        my $line = shift @lines;
        my $curr = $tail - scalar(@lines);
        if ($tbp_1 <= $curr && $curr <= $tbp_2)
        {
            print "$ARGV:" if $opts{F};
            print "$curr:" if $opts{n};
            print $line;
        }
    }
}

# General logic:
# Accumulate each line at end of @lines.
# ** If current line matches, record range that needs printing
# ** When the line array contains enough lines, pop line off front and,
#    if it needs printing, print it.
# At end of file, empty line array, printing requisite accumulated lines.

while (<>)
{
    # Add this line to the accumulated lines
    push @lines, $_;
    $tail = $.;

    printf "# array: N = %d, last = $tail: %s", scalar(@lines), $_ if debug > 1;

    if (m/$op/o)
    {
        # This line matches - set range to be printed
        my $lo = $. - $before;
        $tbp_1 = $lo if ($lo > $tbp_2);
        $tbp_2 = $. + $after;
        print "# $. MATCH: print range $tbp_1 .. $tbp_2\n" if debug;
    }

    # Print out any accumulated lines that need printing
    # Leave $before lines in array.
    print_leaving($before);
}
continue
{
    if (eof)
    {
        # Print out any accumulated lines that need printing
        print_leaving(0);
        # Reset for next file
        close ARGV;
        $tbp_1 = 0;
        $tbp_2 = 0;
        $tail  = 0;
        @lines = ();
    }
}

Pre-Perl Unix解决方案(使用普通的edsedsort -尽管它使用当时不一定可用的getopt ):

代码语言:javascript
复制
#!/bin/ksh
#
# @(#)$Id: old.sgrep.sh,v 1.5 2007/09/15 22:15:43 jleffler Exp $
#
#   Special grep
#   Finds a pattern and prints lines either side of the pattern
#   Line numbers are always produced by ed (substitute for grep),
#   which allows us to eliminate duplicate lines cleanly.  If the
#   user did not ask for numbers, these are then stripped out.
#
#   BUG: if the pattern occurs in in the first line or two and
#   the number of lines to go back is larger than the line number,
#   it fails dismally.

set -- `getopt "f:b:hn" "$@"`

case $# in
0)  echo "Usage: $0 [-hn] [-f x] [-b y] pattern [files]" >&2
    exit 1;;
esac

# Tab required - at least with sed (perl would be different)
# But then the whole problem would be different if implemented in Perl.
number="'s/^\\([0-9][0-9]*\\)       /\\1:/'"
filename="'s%^%%'"      # No-op for sed

f=3
b=3
nflag=no
hflag=no
while [ $# -gt 0 ]
do
    case $1 in
    -f) f=$2; shift 2;;
    -b) b=$2; shift 2;;
    -n) nflag=yes; shift;;
    -h) hflag=yes; shift;;
    --) shift; break;;
    *)  echo "Unknown option $1" >&2
        exit 1;;
    esac
done
pattern="${1:?'No pattern'}"
shift

case $# in
0)  tmp=${TMPDIR:-/tmp}/`basename $0`.$$
    trap "rm -f $tmp ; exit 1" 0
    cat - >$tmp
    set -- $tmp
    sort="sort -t: -u +0n -1"
    ;;
*)  filename="'s%^%'\$file:%"
    sort="sort -t: -u +1n -2"
    ;;
esac

case $nflag in
yes)    num_remove='s/[0-9][0-9]*://';;
no)     num_remove='s/^//';;
esac
case $hflag in
yes)    fileremove='s%^$file:%%';;
no)     fileremove='s/^//';;
esac

for file in $*
do
    echo "g/$pattern/.-${b},.+${f}n" |
    ed - $file |
    eval sed -e "$number" -e "$filename" |
    $sort |
    eval sed -e "$fileremove" -e "$num_remove"
done

rm -f $tmp
trap 0
exit 0

sgrep的外壳版本是在1989年2月编写的,并在1989年5月修复了错误。然后它保持不变,除了1997年的一次管理更改(从SCCS到RCS的转换),直到2007年我添加了-h选项。我在2007年切换到Perl版本。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/383633

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档