match to end of line in bash regex with =~

Viewed 1677

I am trying to use bash regex match to parse a line out of a multi-line string. Why does the following not work?

if [[ $qout =~ ${q}[^\n]* ]] ; then

It matches up to the letter n. I tried using \\n or even \\\n, but nothing worked.

$qout contains:

unrouted    0   0   running
dead-letter 0   0   running

$q contains unrouted.

${BASH_REMATCH[0] evaluates to:

unrouted    0   0   ru

What am I doing wrong?

2 Answers

[\n] doesn't match a newline -- it matches either a literal backslash, or an n. If you run nl=$'\n' and then use $nl is your regex, things will work as-intended.

#!/usr/bin/env bash
qout='unrouted    0   0   running
dead-letter 0   0   running'

q=unrouted

nl=$'\n'
if [[ $qout =~ ${q}[^$nl]*[$nl] ]]; then
  declare -p BASH_REMATCH
else
  echo "No match found" >&2
fi

...emits as output:

declare -ar BASH_REMATCH=([0]=$'unrouted    0   0   running\n')

Assumptions:

  • from a multi-line variable (qout) extract a line that contains a pattern (q)
  • extract from the pattern (q) to the end of the line (to be captured in ${BASH_REMATCH[0]})

A slight modification to the multi-line variable:

$ echo ${qout)
this is unrouted    0   0   running
dead-letter    0   0   running

NOTE: I added the 'this is' prefix to first line so we can verify where the parsing starts.

One idea - borrowing from this answer (shell regex to end of line):

$ sfx=$'[^\n]*'                  # match up to first newline (`\n`) character;
                                 # must use single quotes;
                                 # double quotes will cause the `\n` to be evaluated as the character `n`

$ [[ $qout =~ ${q}${sfx} ]] && echo 'match'
match
$ echo "${BASH_REMATCH[0]}"
unrouted    0   0   running

FWIW, wrapping the test/echo in a set -/+ xv shows:

$ [[ $qout =~ ${q}${sfx} ]] && echo 'match'
+ [[ this is unrouted    0   0   running
dead-letter    0   0   running =~ unrouted[^
]* ]]
+ echo match
match

Adding @David C. Rankin's comment to the mix (placing the \n directly in the regex):

$ [[ $qout =~ ${q}[^$'\n']* ]] && echo 'match'
match
$ echo "${BASH_REMATCH[0]}"
unrouted    0   0   running

NOTE: I can remove this if David wants to post his own answer.

Related