I run several substitution commands as the core of a colorize script for maven. One of the sed
commands uses a regular expression which works find in the shell as discussed here. The current (not working) implementation can be found here.
When I include one of the variants of the command into the script different behavior occurs:
Variant 1:
$ sed -re "s/([a-zA-Z0-9./\\ :-]+)/\1/g"
Adapted to the script:
-re "s/WARNING: ([a-zA-Z0-9./\\ :-]+)/${warn}WARNING: \1${c_end}/g" \
Error: The shell outputs the same information as if I would type $ sed
. Strange!?
Variant 2:
$ sed -e "s/\([a-zA-Z0-9./\\ :-]\+\)/\1/g"
Adapted to the script:
-e "s/WARNING: \([a-zA-Z0-9./\\ :-]\+\)/${warn}WARNING: \1${c_end}/g" \
Error:
sed: -e expression #7, char 59: invalid reference \1 on `s' command's RHS
-i
(edit in place option) with -re
, resulting in -ire
(so that -i
was consuming the re
fragment as its SUFFIX
argument and hence the extended regex mode was not being enabled); changing it to -i -re
fixed the issue.
'
and double quotes "
are treated slightly different, especially when interpreting $vars
. For example: sudo sh -c "sed -r -i 's/(^.+_supplicant.conf)/\1${MTXT}/' /etc/network/interfaces"
works, but: sudo sh -c 'sed -r -i "s/(^.+_supplicant.conf)/\1${MTXT}/" /etc/network/interfaces'
does not.
Don't you need to actually capture for that to work? i.e. for variant #2:
-r -e "s/WARNING: (\([a-zA-Z0-9./\\ :-]\+\))/${warn}WARNING: \1${c_end}/g" \
(Note: untested)
Without the -r
argument back-references (like \1
) won't work unless each parenthesis is escaped with a \
character.
With -r
, argument back-references (like \1
) won't work unless the parenthesis are NOT escaped.
This error is common for parentheses that are not escaped. Escape them and try again.
For example:
/^$/b
:loop
$!{
N
/\n$/!b loop
}
s/\n(.)/\1/g
Should be escaped with backslashes before each parenthesis:
/^$/b
:loop
$!{
N
/\n$/!b loop
}
s/\n\(.\)/\1/g
-r
you don't have to escape the parentheses.
If the -r
/--regexp-extended
option is not provided, then the capturing parentheses must be escaped.
You need escape the /
after the .
sed -e "s/\([a-zA-Z0-9.\/\\ :-]\+\)/\1/g"
Or if you don't want to worry about escaping, use |
sed -e "s|\([a-zA-Z0-9./\\ :-]\+\)|\1|g"
EDIT:
sed -e "s|WARNING: \([a-zA-Z0-9.-/\\ :]+\)|${warn}WARNING: \1${c_end}|g"
sed: -e expression #7, char 58: Invalid range end
. @Denis' answer works.
Success story sharing
-r
option to sed appears to be necessary for the back-reference to work. E.gsed -e 's/([[:digit:]])/is a digit/'
works butsed -e 's/([[:digit:]])/\1 is a digit/
produces the original error without-r
to sed. NOTE: the first invocation of sed searches for a literal(<digit>)
and is not a capture group.-ire
instead of use-ri
. Order matters :-)-r, --regexp-extended
=use extended regular expressions in the script.
In most current versions the-E
and-r
both can be used. Extended as opposed to basic.