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, 2019 at 16:58

4 Answers 4


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, 2019 at 13:51
  • 1
    Why not use <([^>]+)>? I.e. not-> one-or-more times
    – Rich
    Mar 19, 2019 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, 2019 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, 2019 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. Mar 20, 2019 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, 2020 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.