24

Below is the text in the file:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

I need to grep for "42B" and get the output from the above text like:

Pseudo name=Apple
Code=42B
state=fault

Does anyone have idea on how to achieve this using grep/awk/sed?

38

With awk

awk -v RS='' '/42B/' file

RS= changes the input record separator from a newline to blank lines. If any field in an record contains /42B/ print the record.

'' (the null string) is a magic value used to represent blank lines according to POSIX:

If RS is null, then records are separated by sequences consisting of a <newline> plus one or more blank lines, leading or trailing blank lines shall not result in empty records at the beginning or end of the input, and a <newline> shall always be a field separator, no matter what the value of FS is.

The output paragraphs will not be separated since the output separator remains a single newline. To ensure that there is a blank line between output paragraphs, set the output record separator to two newlines:

awk -v RS='' -v ORS='\n\n' '/42B/' file
  • 1
    +1 for an elegant solution. You don't need to redirect the file though... – jasonwryan Jul 14 '13 at 3:53
  • fingers were on autopilot. – llua Jul 14 '13 at 14:16
  • 2
    @jasonwryan, unless you need access to the file name within awk (FILENAME), it's not a bad idea to use redirection as that avoids problems for filename containing = or starting with - (or being -), makes for consistent error messages, and avoids running awk or performing other redirections if the input file can't be opened. – Stéphane Chazelas Nov 17 '15 at 10:22
14

Assuming the data is structured so that it's always the line before and after that you want you can make use of grep's -A (after) and -B (before) switches to tell it to include the 1 line before the match and 1 line after it:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

If you want the same number lines before and after the search term you can use the -C (context) switch:

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

If you'd like to be more stringent when matching the multiple lines you can use the tool pcregrep, to match a pattern over multiple lines:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

The above pattern matches as follows:

  • -M - multiple lines
  • 'Pseudo.*\n.*42B.*\nstate.*' - matches a group of strings where the first string starts with the word "Pseudo" followed by any characters up until a end of line \n, followed by any characters up until the string "42B" followed by any characters up until another end of line (\n), followed by the string "state"followed by any characters.
  • 5
    -C (context) can be used as a shortcut, if -A and -B are the same. – David Baggerman Jul 14 '13 at 0:37
  • @DavidBaggerman - thanks. Added it to the answer. – slm Jul 14 '13 at 0:39
  • Why the one down vote? This answers the question. – slm Jul 14 '13 at 12:07
4

There is probably a similarly easy way to do it with awk, but in perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

That basically says to split the file into chunks delimited by blank lines, then to only print those chunks that match your regular expression.

  • 10
    This can be simplified by using options and shorthands, and losing the useless use of cat; perl -00 -ne 'print if /42B/' file – tripleee Jul 14 '13 at 7:02
4

The grep of some flavours of Unix have the -p flag for "paragraph". I know AIX does.

grep -p 42B <myfile>

would do exactly what you're asking for there. YMMV and GNU grep doesn't have this flag.

  • Having the -p flag would be wonderful. Especially if used together with -v so you could exclude entire paragraphs from output. – IllvilJa Jan 15 at 13:54
2

An other perl solution, without a trailing empty line:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Example

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good
  • 1
    Or shorter (and thus more readable), as triplee wrote in a comment: perl -00 -ne 'print if /42B/' file. – mivk Jan 4 '16 at 15:49

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.