Allan Orozco
Allan Orozco

Reputation: 1

greping two different things from a tail command

Is there a way to grep 2 instances of one word or 1 instance of another word in a single cmd line with perl (perl is a must)

I am trying to do a tail -f file | grep -m 2 word_x OR grep word_y

I am trying to catch word_x two times in the file, if that happens then the tail stops. But word_x doesnt always appear, so i like to have another word_y to stop the tail command.

if word_x appears 2 or more times abort the tail;
if word_x appears only 1 time, look for one ocurrance of word_y and abort the tail;
if word_x doesnt appear but word y appears abort the tail;

Word_y is the last word that will always appear on my file.

So it can be like this 3 examples

text
text
word_x text word_x
test
word_y

text
text
text word_x
test
word_y

text
text
text
word_y

I need to tail the file. i can use other command it doesnt need to be grep.

Is this possible? i have tried several things but cant come with a simple solution, maybe its not possible to do it on a single command line instruction.

Upvotes: 0

Views: 75

Answers (2)

Ed Morton
Ed Morton

Reputation: 204426

Without sample input/output it's a guess but it SOUNDS like this is what you're looking for:

tail file | awk '{x+=gsub(/word_x/,"&")} x==2||/word_y/{exit} 1'

Depending on the requirements you haven't shared with us yet, you may need word boundaries too, e.g. with GNU awk:

tail file | awk '{x+=gsub(/\<word_x\>/,"&")} x==2||/\<word_y\>/{exit} 1'

If you only want to consider cases where word_x occurs twice on one line then it's briefer:

tail file | awk 'gsub(/\<word_x\>/,"&")==2||/\<word_y\>/{exit} 1'

Upvotes: 0

Craig Estey
Craig Estey

Reputation: 33631

(1) if word_x appears 2 or more times abort the tail

That's clear. But, the following is inconsistent

(2) if word_x appears only 1 time, look for word_y

This says look for word_y only if the line has a single occurence of word_x

(3) and abort the tail if word_x doesnt appear but word y appears abort the tail

However, this says only look for word_y if word_x does not appear.

(2) and (3) appear to conflict. To me, ignoring (2) and using (3) makes the most sense.

Also, you didn't say whether you wanted the "abort" line to be passed on or not.

Here's some code that I believe will work. I did some minimal testing on it

#!/usr/bin/perl

my($word_x) = shift(@ARGV);
my($word_y) = shift(@ARGV);

# quirk of perl -- the regex needs the longest to be first in order if had
# similar words like "abc" and "abcd"
my(@rgx) = ($word_x,$word_y);
@rgx = sort({length($b) <=> length($a)} @rgx);
my($rgx) = join("|",@rgx);

while (my $buf = <STDIN>) {
    chomp($buf);

    # NOTE: this assumes the "abort" line should be printed
    print($buf,"\n");

    # get frequency of each word
    # NOTE: this grabs partials, so it may need \b or \W wrappers
    my(%freq);
    while ($buf =~ /($rgx)/go) {
        $freq{$1} += 1;
    }

    my $xcnt = $freq{$word_x};

    # got two or more of x -- we're done
    last if ($xcnt >= 2);

    # only look for y if x does _not_ appear at all
    if ($xcnt == 0) {
        # found a y -- we're done
        last if ($freq{$word_y} >= 1);
    }

    # NOTE: this assumes the "abort" line should _not_ be printed
    # use either of the prints but _not_ both
    ###print($buf,"\n");
}

Upvotes: 0

Related Questions