Save part of matching pattern to variable

Ben Ruijl picture Ben Ruijl · Apr 12, 2010 · Viewed 35.1k times · Source

I want to extract a substring matching a pattern and save it to a file. An example string:

Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk

I want to extract the part between the brackets, in this case [sdf].

I tried to do something like grep -e '[$subtext]' to save the text in the brackets to a variable. Of course it doesn't work, but I am looking for a way similar to this. It would be very elegant to include a variable in a regex like this. What can I do best?

Thanks!

Answer

Charles Duffy picture Charles Duffy · Apr 13, 2010

BASH_REMATCH is an array containing groups matched by the shell.

$ line='Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk'
$ [[ $line =~ \[([^]]+)\] ]]; echo "${BASH_REMATCH[1]}"
sdf

If you want to put this in a loop, you can do that; here's an example:

while read -r line; do
  if [[ $line =~ \[([^]]+)\] ]] ; then
    drive="${BASH_REMATCH[1]}"
    do_something_with "$drive"
  fi
done < <(dmesg | egrep '\[([hsv]d[^]]+)\]')

This approach puts no external calls into the loop -- so the shell doesn't need to fork and exec to start external programs such as sed or grep. As such, it is arguably significantly cleaner than other approaches offered here.

BTW, your initial approach (using grep) was not that far off; using grep -o will output only the matching substring:

$ subtext=$(egrep -o "\[[^]]*\]" <<<"$line")

...though this includes the brackets inside the capture, and thus is not 100% correct.