9

I have this file:

sometext1{
string1
}

sometext2{
string2
string3
}

sometext3{
string4
string5
string6
}

I want to search this file for a specific string and print everything before this string up to the opening { and everything after this string up to the closing }. I tried to achieve this with sed but if I try to print everything in the range /{/,/string2/ for example sed prints this:

sometext1{
string1
}

sometext2{
string2
sometext3{
string4
string5
string6
}

If I search for the string "string2" I need the output to be:

sometext2{
string2
string3
}

Thanks.

  • Well, now I found that I need the line numbers of the ouput in the original file to delete them later. I tried changing the command that @mikeserv supplied with no luck, I'm a bit confused with sed's hold function. – rodrigo Jan 5 '15 at 18:42
  • well, geez, rodrigo, you didnt tell anyone that but yourself. it can be done, but it is best done like grep -n '' <infile | sed .... The sed commands will need modifying; specifically the /address/ bits that look for ^top-of-line anchors. So, if you were using my answer you could probably do: grep -n '' | sed 'H;/{$/h;/^[^:]*:}/x;/{\n.*PATTERN/!d'. All output lines will be prefixed with the original file's line numbers followed by a colon like 1:sometext1{\n2:string1 and so on. sed will filter only what it would filter before, except that each output line opens with a number. – mikeserv Sep 28 '15 at 11:14
9

Here are two commands. If you want a command that trims up to the last .*{$ line in a sequence (as @don_crissti does with ed) you can do:

sed 'H;/{$/h;/^}/x;/{\n.*PATTERN/!d'

...which works by appending every line to Hold space following a \newline character, overwriting hold space for every line that matches {$, and swapping ing hold and pattern spaces for every line that matches ^} - and thereby flushing its buffer.

It only prints lines which match a { then a \newline and then PATTERN at some point - and that only ever happens immediately following a buffer swap.

It elides any lines in a series of {$ matches to the last in the sequence, but you can get all of those inclusive like:

sed '/PATTERN.*\n/p;//g;/{$/,/^}/H;//x;D'

What it does is swap pattern and hold spaces for every ...{$.*^}.* sequence, appends all lines within the sequence to Hold space following a \newline character, and Deletes up to the first occurring \newline character in pattern space for every line cycle before starting again with what remains.

Of course, the only time it ever gets \newline in pattern space is when an input line matches ^} - the end of your range - and so when it reruns the script on any other occasion it just pulls in the next input line per usual.

When PATTERN is found in the same pattern space as a \newline, though, it prints the lot before overwriting it with ^} again (so it can end the range and flush the buffer).

Given this input file (thanks don):

sometext1{
string1
}

sometext2{
PATTERN
string3
}

sometext3{
string4
string5
string6
}

Header{
sometext4{
some string

string unknown

here's PATTERN and PATTERN again
and PATTERN too
another string here
}
}

The first prints:

sometext2{
PATTERN
string3
}
sometext4{
some string

string unknown

here's PATTERN and PATTERN again
and PATTERN too
another string here
}

...and the second...

sometext2{
PATTERN
string3
}
Header{
sometext4{
some string

string unknown

here's PATTERN and PATTERN again
and PATTERN too
another string here
}
  • @don_crissti - I dunno. It only delimits the sequence for a line beginning with }. This could be beneficial for like... open{\nsub;\n{ command; }\n}; close - but I'm not certain that's what is going on here... – mikeserv Dec 19 '14 at 17:51
  • Hi @mikeserv - I've similar question that is raised here unix.stackexchange.com/questions/232509/…, your solution works on small file, but I have a large file and I am getting "Hold space overflowed." error message. Any chance do you know, how could I resolve this? Many thanks – Narayan Akhade Sep 28 '15 at 12:58
  • @NarayanAkhade - no. not without an overhaul, anyway. unless... are there large expanses of input which are not contained with {...} blocks? If that's the case and you're using the first solution then you might do /{$/,/^}/H at the start instead of just H. But if you also tried the second solution and still encountered the same error it's not likely to help because that one already does that. And don't discount ed, either. don's got a very good answer here, and ed can be applied to use temporary buffer files very simply as well, which should prevent mem buffer overruns. – mikeserv Sep 28 '15 at 13:15
6

Here's a solution with ed:

ed -s filename <<< $'g/PATTERN/?{?,/}/p\nq\n'

that is:

g/PATTERN/     # mark each line matching PATTERN  
?{?,/}/p       # for each marked line, print all lines from the previous { up to the next }  
q              # quit editor

This assumes there's only one line with PATTERN between each pair of { } otherwise you will get duplicate output for each additional line with PATTERN inside the same block.
It will work for multiple { } containing a single line matching PATTERN e.g. for a test file with PATTERN in two different sections:

sometext1{
string1
}

sometext2{
PATTERN
string3
}

sometext3{
string4
string5
string6
}

Header{
sometext4{
some string

string unknown

here's PATTERN again

another string here
}
}

running

ed -s sample <<< $'g/PATTERN/?{?,/}/p\nq\n'

outputs:

sometext2{
PATTERN
string3
}
sometext4{
some string

string unknown

here's PATTERN again

another string here
}
  • I took a lot from this, actually! Thanks very much! – mikeserv Dec 19 '14 at 15:18
  • I dind't even know this command exists. Thanks – rodrigo Dec 22 '14 at 11:30
4

With pcregrep:

pcregrep -M '(?s)\{[^}]*PATTERN.*?\}'

Or with GNU grep provided the input doesn't contain NUL bytes:

grep -Poz '.*(?s)\{[^}]*PATTERN.*?\}'
0
$ awk 'BEGIN{RS="\n\n"; FS="[{}]"} {if ($2 ~ /string4/) {print $2}}' t1.txt
string4
string5
string6

where:

  • string4 --> string to be matched
  • t1.txt --> contains the file content mentioned in the query
-2

sed -n '/string/p' filename

the -n when added to sed suppressed sed's default behavior this statement might not give you exactly what you want but it should just displace the string

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.