sed find string , print everything until next pattern

Viewed 161

We have the following scenario that we inherited.

A file with 'hosts per service' is piped into sed.

Based on the service name the list of hosts would be returned.

Command used:

/bin/sh -c 'cat file.txt | sed -n "/main-service]/,/\\[/{/\\[/!p;}"'

OS(s): Ubuntu 14 16 18 20

On the road we found out that sed is returning more hosts that what we expect.

I'm trying to fix this 'greedy' behavior without modifying the 1st part of the sed ( '/main-service]/' ),

A fix for this can be: '/\[main-service]/', but I do not know exactly what it could brake ( this sed is used in a lot of places ), so I'm trying to avoid this edit.

File example ( Sed input ):

[main-service]
hosta
hostb
hostc

[that-main-service]
hostd
hoste


[other-main-service]
hostf
hostg

In the below example we are looking for all the hosts under the 'main-service'

/bin/sh -c 'cat file.txt | sed -n "/main-service]/,/\\[/{/\\[/!p;}"'

However, the output is not the one that we expect

hosta
hostb
hostc

hostf
hostg

I do not know what the creator meant with the sed syntax exactly, but what I'm trying to achieve is:

if service name == '/main-service]/' #find only the line that matches

take all the text until '\n\['

Example here:

https://sed.js.org/?gist=0a90e4d015d02b820072e6cf837e6204

Where the expected output is:

hosta
hostb
hostc

Any help would be appreciated.

Thank you.

4 Answers

With your shown samples could you please try following in awk.

awk '/^\[/ && found{exit} /^\[main-service\]/{found=1;next} found && NF' Input_file

Output will be as follows.

hosta
hostb
hostc

Explanation: Adding detailed explanation for above.

awk '                 ##Starting awk program from here.
/^\[/ && found{       ##Checking condition if line starts from [ and found is NOT NULL then do following.
  exit                ##exit from this program, no need to read whole Input_file.
}
/^\[main-service\]/{  ##Checking condition if line starts from [ main-service] then do following.
  found=1             ##Setting found to 1 here.
  next                ##next will skip all further statements from here.
}
found && NF           ##Checking condition if found is SET and NF is NOT NULL then print that line.
' Input_file          ##Mentioning Input_file name here.
sed -n '/\[main-service]/,/^$/{//!p}'

would be the best fit, but you mention you are reluctant to use \[

so, you can quit on the first such match using

sed -n '/main-service]/,/^$/{//!p; /^$/q}'

Also, you can directly pass the file name to sed, no need to use cat and I'm not sure why you are using /bin/sh -c instead of calling sed directly.

This might work for you (GNU sed):

/bin/sh -c 'cat file | sed -n "/main-service]/{n;:a;p;n;/\\[/q;ba}"'

This uses the same regexps but manipulates the program flow.

N.B. The original sed command may have appended more than was intended as the stopping point for the printing depends on another stanza being present or the end of file condition.

It might help to reconsider the delimiters you have chosen for the range. Because the ] and [ brackets are on the same line, the logic is messy.

Use the full pattern of the section header to match the beginning of the range /^\[main-service]/ and the blank line to match the end of the range /^$/

sed -n  '/^\[main-service]/,/^$/p' services.txt | sed '1d;$d;'
  • -n option to suppress automatic printing.
  • p command to print the lines of the range

Since the result includes the header and the blank line, we pipe the result into a second command to remove the first and last line sed '1d;$d;'

Related