3

Still struggling with messy logfiles. What I want is to find lines that first matching string X and then string Y. I then want to print them together. The problem is that, sometimes there is X, but no Y.

Example input

31 X
32 Y
33 X
34 Y
35 X
36 X
37 Y
38 X
39 X

Expected output

31 X
32 Y
33 X
34 Y
36 X
37 Y

So, lines 35, 38 and 39 are omitted because there is no string Y.

My starting is point is this:

cat $filename | grep -E X\|Y | grep -A1 'X'

But this does not filter out lines 35, 38 and 39. What I want is a conditional: only print the lines X and then Y if there is Y. If not, print nothing.

  • Add |uniq -f1 to your pipeline. – mikeserv Jul 2 '15 at 20:03
  • uniq can read files as well. uniq -f1 filename will work just fine – Valentin Bajrami Jul 2 '15 at 20:39
  • @val0x00ff - well, not exactly, unless the only lines in file are X/Y lines. grep or something is till required, and is probably the faster of the filters, and so should go first. – mikeserv Jul 2 '15 at 21:07
2

If I understand you correctly, you only want lines containing X if they are then followed by a line containing Y, and then you want both lines.

grep -A1 X filename |grep --no-group-separator -B1 Y

  • This does exactly what I want, and I can see why. – Rogier Visser Jul 3 '15 at 18:27
4

Using sed:

sed -n ':b /X/ { h; n; /Y/! b b; H; x; p; }'

Output:

31 X
32 Y
33 X
34 Y
36 X
37 Y
  • Thanks, I always thought about branching with sed but never used it. Finally I got an example to work with. – ikrabbe Jul 3 '15 at 10:02
  • +, though you could do without the branching: sed '/Y/!{h;d};//{x;/X/!d;G}' infile – don_crissti Aug 23 '15 at 0:32
3

Another sed:

$ sed -e '/X/{
    $!N
    /\n.*Y/!D
}' file
31 X
32 Y
33 X
34 Y
36 X
37 Y
3

There are various tools that can do this. If you have pcregrep (it should be available in your distribution's repository), you can do:

$ pcregrep -M 'X\n[^\n]+Y' file 
31 X
32 Y
33 X
34 Y
36 X
37 Y

The -M switch allows pattern to match across newline characters and the regular expression matches an X, followed by a newline, then any non-newline characters and a Y.


Another option is to write a little script that saves the previous line if it matches X and prints it along with the current one if the current line matches Y. For example, in awk:

$ awk '{if(last~/X/ && /Y/){print last"\n"$0}last=$0}' file 
31 X
32 Y
33 X
34 Y
36 X
37 Y

Or Perl:

$ perl -ne '$last=~/X/ && /Y/ && print "$last$_"; $last=$_' file 
31 X
32 Y
33 X
34 Y
36 X
37 Y
0

Since the question was tagged with awk:

$ awk 'p2 == "X" && $2 == "Y" { print p; print } { p = $0; p2 = $2 }' file
31 X
32 Y
33 X
34 Y
36 X
37 Y

This prints an X line and the immediately following Y line, if there is one (or neither of them would be printed).

The variables p and p2 holds the unmodified previous line and its second column respectively.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.