In a (BSD) UNIX environment, I would like to capture a specific substring using a regular expression.

Assume that the dmesg command output would include the following line:

pass2: <Marvell Console 1.01> Removable Processor SCSI device

I would like to capture the text between the < and > characters, like

dmesg | <sed command>

should output:

Marvell Console 1.01

However, it should not output anything if the regex does not match. Many solutions including sed -e 's/$regex/\1/ will output the whole input if no match is found, which is not what i want.

The corresponding regexp could be: regex="^pass2\: \<(.*)\>"

How would i properly do a regex match using sed or grep? Note that the grep -P option is unavailable in my BSD UNIX distribution. The sed -E option is available, however.

  • It's possibly better to parse the output of camcontrol devlist than the output of dmesg. – JdeBP Mar 19 '19 at 16:58

Try this,

sed -nE 's/^pass2:.*<(.*)>.*$/\1/p'

Or POSIXly (-E has not made it to the POSIX standard yet as of 2019):

sed -n 's/^pass2:.*<\(.*\)>.*$/\1/p'


$ printf '%s\n' 'pass2: <Marvell Console 1.01> Removable Processor SCSI device' | sed -nE 's/^pass2:.*<(.*)>.*$/\1/p'
Marvell Console 1.01

This will only print the last occurrence of <...> for each line.

  • This works for me, with both the -n parameter and the /p suffix inside the regex. Full command i used: dmesg | sed -nE 's/^pass2: <(.*)>.*$/\1/p – Steiner Mar 19 '19 at 13:51
  • 1
    Why not use <([^>]+)>? I.e. not-> one-or-more times – Rich Mar 19 '19 at 17:29
  • 1
    sed regex is per default non-greedy, so it's not necessary here. But it would work, too. – pLumo Mar 20 '19 at 7:02

How about -o under grep to just print the matching part? We still need to remove the <>, though, but tr works there.

dmesg |egrep -o "<([a-zA-Z\.0-9 ]+)>" |tr -d "<>"
Marvell Console 1.01

I tried below 3 methods by using sed, awk and python

sed command

echo "pass2: <Marvell Console 1.01> Removable Processor SCSI device" | sed "s/.*<//g"|sed "s/>.*//g"


Marvell Console 1.01

awk command

echo "pass2: <Marvell Console 1.01> Removable Processor SCSI device" | awk -F "[<>]" '{print $2}'


Marvell Console 1.01


import re
for i in l:
    o=i.split(' ')
    for i in o[1:4]:
print (" ".join(h)).replace('>','').replace('<','')


Marvell Console 1.01
  • I was thinking the awk approach too. Should you constrain your print to lines beginning with "pass2:"? The OP didn't provide sufficient detail, but I can imagine that a naive pattern match would not be quite what was wanted. – jwm Mar 20 '19 at 0:00
  • Python can read from standard in, though perl specializes in this kind of text processing if you’re moving into higher level scripting languages. – D. Ben Knoble Mar 20 '19 at 3:48

You can extract substring with below grep -o -e command:

cat some.log | grep "lineWithThisText" | grep -o -e 'SomeSequence1[0-9]*[A-Z]*SomeSequence2'

For some reason * works rather than + for 1 or many match in this grep regex match command.

Read grep manual with the following command:

man grep

Read about options -o and -e.

I use this at work to extract tons of data from several log lines.

  • Welcome to the site, and thank you for your contribution. The reason that + doesn't seem to work is that by default, grep interprets the regular expression as basic regular expression, which doesn't include the +. You will have to use the -E option in order to enable them (at least on GNU grep), or use egrep instead. – AdminBee Apr 29 '20 at 16:11

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.