10

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.

1
  • 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

12

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'

Output:

$ 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.

3
  • 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
9

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
4

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"

output

Marvell Console 1.01

awk command

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

output

Marvell Console 1.01

python

#!/usr/bin/python
import re
h=[]
k=open('l.txt','r')
l=k.readlines()
for i in l:
    o=i.split(' ')
    for i in o[1:4]:
        h.append(i)
print (" ".join(h)).replace('>','').replace('<','')

output

Marvell Console 1.01
2
  • 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
0

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.

1
  • 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

You must log in to answer this question.

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