Skip to content
shellhacks.dev
Bash

Use a variable as the replacement safely

In Bash 5.2 an ampersand in the replacement of ${v//old/new} stands for the matched text. Quote the replacement or turn off patsub_replacement.

1 min read
Summarize in AI

A substitution that worked in Bash 5.1 now prints the match where a literal ampersand should be, and this is how to get the old behaviour back.

Shell
v="cat and cat"
r='[&]'
echo "${v//cat/$r}"
echo "${v//cat/"$r"}"
shopt -u patsub_replacement
echo "${v//cat/$r}"
Text
[cat] and [cat]
[&] and [&]
[&] and [&]

Bash 5.2 added the patsub_replacement option, which is on by default and makes an unquoted ampersand in the replacement string mean "the text that matched", the way it does in sed. The option is listed in the shopt section of the manual. Quoting the expansion inside the replacement keeps the ampersand literal, and shopt -u patsub_replacement removes the feature for the rest of the script.

Bash 5.1 and earlier treat the ampersand as an ordinary character, so a script that relies on the new meaning does not run there. Quote replacements that come from user input, or a stray ampersand rewrites the output.

Share

More snippets

Bash

Strip a file extension

Strip a file extension in Bash with ${f%.*}, which removes only the shortest suffix after the last dot and needs no basename call or subshell.

1 min read
Bash

Strip every extension from a name

Remove every extension from a name with ${f%%.*} in Bash. The doubled percent sign takes the longest match, where a single one stops at the last dot.

1 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.