Does bash support back references in parameter expansion?

ksh93 and zsh have back-reference (or more accurately1, references to capture groups in the replacement) support inside ${var/pattern/replacement}, not bash.

ksh93:

$ var='Blah: -> r1-ae0-2 / [123]'
$ printf '%s\n' "${var/*@(->*([[:space:]])+([^[:space:]]))*/\1}"
-> r1-ae0-2

zsh:

$ var='Blah: -> r1-ae0-2 / [123]'
$ set -o extendedglob
$ printf '%s\n' "${var/(#b)*(->[[:space:]]#[^[:space:]]##)*/$match[1]}"
-> r1-ae0-2

(mksh man page also mentions that future versions will support it with ${KSH_MATCH[1]} for the first capture group. Not available yet as of 2017-04-25).

However, with bash, you can do:

$ [[ $var =~ -\>[[:space:]]*[^[:space:]]+ ]] &&
  printf '%s\n' "${BASH_REMATCH[0]}"
-> r1-ae0-2

Which is better as it checks that the pattern is found first.

If your system's regexps support \s/\S, you can also do:

re='->\s*\S+'
[[ $var =~ $re ]]

With zsh, you can get the full power of PCREs with:

$ set -o rematchpcre
$ [[ $var =~ '->\s*\S+' ]] && printf '%s\n' $MATCH
-> r1-ae0-2

With zsh -o extendedglob, see also:

$ printf '%s\n' ${(SM)var##-\>[[:space:]]#[^[:space:]]##}
-> r1-ae0-2

Portably:

$ expr " $var" : '.*\(->[[:space:]]*[^[:space:]]\{1,\}\)'
-> r1-ae0-2

If there are several occurrences of the pattern in the string, the behaviour will vary with all those solutions. However none of them will give you a newline separated list of all matches like in your GNU-grep-based solution.

To do that, you'd need to do the looping by hand. For instance, with bash:

re='(->\s*\S+)(.*)'
while [[ $var =~ $re ]]; do
  printf '%s\n' "${BASH_REMATCH[1]}"
  var=${BASH_REMATCH[2]}
done

With zsh, you could resort to this kind of trick to store all the matches in an array:

set -o extendedglob
matches=() n=0
: ${var//(#m)->[[:space:]]#[^[:space:]]##/${matches[++n]::=$MATCH}}
printf '%s\n' $matches

1 back-references does more commonly designate a pattern that references what was matched by an earlier group. For instance, the \(.\)\1 basic regular expression matches a single character followed by that same character (it matches on aa, not on ab). That \1 is a back-reference to that \(.\) capture group in the same pattern.

ksh93 does support back-references in its patterns (for instance ls -d -- @(?)\1 will list the file names that consist of two identical characters), not other shells. Standard BREs and PCREs support back-references but not standard ERE, though some ERE implementations support it as an extension. bash's [[ foo =~ re ]] uses EREs.

[[ aa =~ (.)\1 ]]

will not match, but

re='(.)\1'; [[ aa =~ $re ]]

may if the system's EREs support it.


You want to delete everything up to the first ␣->␣ (not including the "arrow") and after the last ␣/ (including the space and slash).

string="Blah: -> r1-ae0-2 / [123]"
string=${string/*->/->}
string=${string/ \/*}

$string will now be -> r1-ae0-2.

The same two substitutions would turn -> s7-Gi0-0-1:1-US / Foo into -> s7-Gi0-0-1:1-US.


Answering this definitively is impossible without knowing the exact format every message takes. However, as a general approach you can print certain specific fields using cut:

$ cut -d ' ' -f 2 <<< '-> s7-Gi0-0-1:1-US / Foo'
s7-Gi0-0-1:1-US

Or you can print every nth column using awk:

$ awk -F' ' '{ for (i=2;i<=NF;i+=4) print $i }' <<< '-> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Foo'
r1-ae0-2
s7-Gi0-0-1:1-US