1

I'm searching for one liner command (in best way) that can find if specific string/line is presented between other two lines. I search for it and I only found commad to get content between two lines, but how can i check if someting is present or not..

.....
 1 a 2 b 3
 4
   5
.....
 1 c 2 d 3
 4
   5
.....
 1 e 2 f 3
   5
.....

I found this:

sed -n '/^ 1 .* 2 .* 3$/,/^ 5$/p'

the result with this command cut the unwanted lines good for start (cut lines "...."), but still not know how to check if "4" is presented between :

 1 a 2 b 3
 4
   5
 1 c 2 d 3
 4
   5
 1 e 2 f 3
   5

The output should look like this:

 "4" is missing after "1 e 2 f 3"

OR only (is even better):

"1 e 2 f 3"

2 Answers 2

0

sed is not the right tool for the job. However, we can probably still sort of do it in sed:

sed -n '/^ 1 .* 2 .* 3$/,/^ 5$/ { /^ 1 .* 2 .* 3$/ { h }; /^ 4$/ { x; s/.*//; x;}; /^ 5$/ { x; p; x} }' filename | grep -v -e '^$'

Here's only the part that I added into your range block:

/^ 1 .* 2 .* 3$/ { h }; /^ 4$/ { x; s/.*//; x;}; /^ 5$/ { x; p; x}

Read this as:

if line matches regex /^ 1 .* 2 .* 3$/, then h (store line into hold buffer)
if line matches regex /^ 4$/, then x (exchange buffer, i.e. make operations apply to hold buffer rather than standard buffer), then replace everything in hold buffer*, then x again (to switch back to standard buffer)
if line matches ^ 5$/, then switch to hold buffer, p (print contents of hold buffer), and switch back to standard buffer

* Unfortunately, s/.*// doesn't delete lines in the hold buffer. Deleting lines in the hold buffer seems difficult, so we get rid of them by piping into grep -v -e '^$' instead.

Update

This version prints the filename after the match (using the F command), and manages without piping into grep. Thank you, Paulo!

sed -n '/^ 1 .* 2 .* 3$/,/^ 5$/ { /^ 1 .* 2 .* 3$/ { h }; /^ 4$/ { x; s/.*//; x;}; /^ 5$/ { x; /^$/ !{ p; F }; x} }' data
7
  • Thanks! works for me, it is little complicate but is not problem :). One question: because I run script from my home dir and the files that have to check are on other dir, but they are a lot... it is possible to print the FILENAME here ?? Because now I't works but I don't know witch result in which file is :(
    – loken
    Mar 24, 2018 at 10:34
  • How exactly are you processing this? I would recommend just echoing the filename on a separate line. If you aren't sure about how to do this, feel free to paste your script and I'll give you a recommendation on where to put the echo.
    – sneep
    Mar 24, 2018 at 10:46
  • 1
    grep -v -e '^$' could be like this in sed /^$/d (or /^$/!p since -n option is present). GNUsed has the F command to print file name input 1F will print it as first line of output.
    – Paulo
    Mar 24, 2018 at 13:56
  • I tried using d, but that didn't seem to work on the holding buffer, and it also seemed to jump out of the block and start processing the next line. I'll incorporate the other suggestions though! Thanks a lot!
    – sneep
    Mar 24, 2018 at 14:52
  • Thanks sneep !!! working fine. Can you suggest me what other program I can use - awk maybe ? Can someone give AWK solution ?
    – loken
    Mar 26, 2018 at 10:24
0

Thanx @sneep for mentioning perl:

perl -lane 'if($n=/^ 1 .* 2 .* 3$/../^ *5$/) {$s=$_ if $n==1; $s="" if /^ *4$/; print "$ARGV: $s" if $s && $n=~/E/}' /otherdir/*

I hope I understand the problem correctly: please add to the OP to specify any further details.

Explanation:

perl Practical Extraction and Reporting Language.
-lane switches commonly helpful for one-liners.
' start of the actual program instructions
if(/^ 1 .* 2 .* 3$/../^ *5$/) { execute the actions between curly braces only for pieces of text that start with a line matching /^ 1 .* 2 .* 3$/ and end with a line that contains a single 5 digit, possibly preceded by any number of spaces.
$n=/^ 1 .* 2 .* 3$/../^ *5$/ keep track of the line number within the text portion.
$s=$_ if $n==1; save the first line of the text portion in the $s variable.
$s="" if /^ *4$/; look for the desired string: a single 4 digit that comes after any number of spaces. If found, delete the previously saved line for the $s variable.
print "$ARGV: $s" if $s && $n=~/E/ print the filename, a colon, a space and the line that started the current text portion if the variable $s contains any text and the $n counter shows that the last line of the text portion has been reached.
} end of the actions to be performed on the text portion.
' end of the program instructions.
/otherdir/* process all files in the /otherdir/ path

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .