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.
Best Answer
As discussed in the comments, this seems to have changed between versions of Bash. I think this is the relevant change in
bash-4.3-alpha
(changelog):And the description for
shopt -s compat42
(online manual):The quoting single-quotes example:
Workaround: put the replacement string in a variable, and don't use quotes inside the replacement:
The funny thing is, that if the expansion is unquoted, then the quotes are removed after the substitution, in all versions. That is
s=abc; echo ${s/b/""}
printsac
. This of course doesn't happen with other expansions, e.g.s='a""c' ; echo ${s%x}
outputsa""c
.