Skip to main content

Inconsistent behavior of bash string replacement [Resolved]

When I print a variable with one substring replacing another as described in this documentation, it behaves correctly:

stringZ='abc - 123'

echo $stringZ             # abc - 123

echo ${stringZ/ - /test}  # abctest123

However, if I get the string from a filename, the replacement is ignored completely:

for f in mp3/*; do

    stringZ=$(basename $f)

    echo $stringZ             # abc - 123.txt

    echo ${stringZ/ - /test}  # abc - 123.txt


How can I replace a substring in a variable derived from a filename?

Question Credit: Keyslinger
Question Reference
Asked June 12, 2019
Posted Under: Unix Linux
1 Answers

The answer is, as usual, that you need to always quote your variables:

$ ls -N mp3/
abc - 123.txt

Now, let's try your loop:

$ for f in mp3/*; do 
    stringZ=$(basename $f); 
    echo $stringZ; 
    echo ${stringZ/ - /test} ; 
basename: extra operand ‘123.txt’
Try 'basename --help' for more information.

What happens is that the $f variable has the value mp3/abc - 123.txt, so you are running basename mp3/abc - 123.txt and that complains since it sees the space and assumes that mp3/abc is the first argument. It should work as expected if you quote:

$ for f in mp3/*; do 
    stringZ=$(basename "$f"); 
    echo "$stringZ"; 
    echo "${stringZ/ - /test}" ; 
abc - 123.txt

Only the quotes in the basename "$f" are absolutely necessary here, but always quoting your variables is a good habit to get into.

credit: terdon
Answered June 12, 2019
Your Answer