[SOLVED] Fix the Bash Bad Substitution Syntax Error

The Bash bad substitution error is a syntax error that can be caused by different reasons. Two common causes for this error are the incorrect use of command substitution and incorrect characters added to the lines of your script, for example extra dollar signs or white spaces.

For example, this error occurs when you want to use command substitution but by mistake, you use curly braces instead of parentheses.

Command substitution allows you to store the output of a Bash command into a variable.

I created a simple example to show you how this error occurs when using command substitution incorrectly.

Let’s use command substitution to store the output of the command ls -al in the variable OUTPUT.

#!/bin/bash

OUTPUT=${ls -al}
echo "$OUTPUT"

When I execute the code I see the following output:

(localhost)$ ./subst.sh
./subst.sh: line 3: ${ls -al}: bad substitution

According to the error, there’s something wrong with line 3.

I used ${ } around the ls command while command substitution requires $( ) around the command.

Let’s replace curly brackets with parentheses and rerun the program:

#!/bin/bash

OUTPUT=$(ls -al)
echo "$OUTPUT"

Now the output is:

(localhost)$ ./subst.sh
total 8
drwxr-xr-x   4 myuser  mygroup  128 Jul 26 12:57 .
drwxr-xr-x  29 myuser  mygroup  928 Jul 26 12:54 ..
-rwxr-xr-x   1 myuser  mygroup   45 Jul 26 12:56 subst.sh
-rw-r--r--   1 myuser  mygroup    0 Jul 26 12:55 testfile

This time the script works well. We have fixed the error.

Bad Substitution Error Caused by White Spaces

This error can also be caused by the presence of white spaces in your code that go against Bash syntax.

I have modified the last line of the script we have created in the previous section:

#!/bin/bash

OUTPUT=$(ls -al)
echo "${OUTPUT }"

I added curly brackets around the OUTPUT variable, I also added a white space before closing the curly bracket to show you what happens.

When I execute the code I get the following error from the Linux shell:

(localhost)$ ./subst.sh
./subst.sh: line 4: ${OUTPUT }: bad substitution

The error message tells the line that is causing the error.

To solve the problem remove the white space before the closing curly brackets.

If you see the bad substitution error ensure your command doesn’t contain any incorrect white spaces.

Dollar Sign used Twice when Printing the Value of a Variable

Another common cause for this error is using the dollar sign twice when you print the value of a variable in your script.

Here is the program used previously, but modified to introduce a syntax error on the last line:

#!/bin/sh

OUTPUT=$(ls -al)
echo "${$OUTPUT}"

As you can see I have used the $ sign twice on the line where I use the echo command. The error is the following:

(localhost)$ ./subst.sh
./subst.sh: line 4: ${$OUTPUT}: bad substitution

The solution is to remove the extra $ sign inside the curly brackets.

Bad Substitution Error and String Variables

Let’s see what happens if you have multiple strings in your echo command.

In the next example, we will print the full path of a directory created from the concatenation of three different variables: BASE_DIR, APP_DIR, and CONF_DIR.

#!/bin/bash

BASE_DIR=/opt/base
APP_DIR=app
CONF_DIR=conf
echo ${$BASE_DIR/$APP_DIR/$CONF_DIR}

Let’s run the code:

(localhost)$ ./full_path.sh
./full_path.sh: line 6: ${$BASE_DIR/$APP_DIR/$CONF_DIR}: bad substitution

What’s causing the error?

We see a similar error to the one in the previous section, caused by an extra dollar sign that the Bash shell doesn’t like.

The difference between this code and the previous one is that in the previous one we only had one variable within curly brackets.

Let’s see how we can fix the error in this script:

Option 1: Remove the outer $ sign from the last line:

#!/bin/bash

BASE_DIR=/opt/base
APP_DIR=app
CONF_DIR=conf
echo $BASE_DIR/$APP_DIR/$CONF_DIR

The output is:

(localhost)$ ./full_path.sh
/opt/base/app/conf

The script works well!

Option 2: Here is another possible option:

#!/bin/bash

BASE_DIR=/opt/base
APP_DIR=app
CONF_DIR=conf
echo ${BASE_DIR}/${APP_DIR}/${CONF_DIR}

And the output is correct:

(localhost)$ ./full_path.sh
/opt/base/app/conf

I prefer this version because the three variables are separated and this helps avoid errors.

Bash Bad Substitution when Converting String Variables to Uppercase or Lowercase

Converting strings in your shell script from lowercase to uppercase (or vice versa) is a common requirement.

Recent versions of Bash (e.g. here I’m using Bash 4.4.19) provide a built-in way for uppercasing and lowercasing strings.

Here’s how:

[myuser@localhost ~]$ DAY=monday
[myuser@localhost ~]$ echo ${DAY}
monday
[myuser@localhost ~]$ echo ${DAY^}
Monday
[myuser@localhost ~]$ echo ${DAY^^}
MONDAY

A similar syntax but with , instead of ^ allows to convert strings from uppercase to lowercase:

[myuser@localhost ~]$ DAY=MONDAY
[myuser@localhost ~]$ echo ${DAY}
MONDAY
[myuser@localhost ~]$ echo ${DAY,}
mONDAY
[myuser@localhost ~]$ echo ${DAY,,}
monday

Now let’s try to run this with an earlier version of Bash. Before I was running it on Linux, now let’s try with the Bash shell on Mac:

(localhost)$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.

Here is the error I get when I try to convert the string variable to uppercase:

(localhost)$ DAY=monday
(localhost)$ echo ${DAY}
monday
(localhost)$ echo ${DAY^}
-bash: ${DAY^}: bad substitution
(localhost)$ echo ${DAY^^}
-bash: ${DAY^^}: bad substitution

In both cases, we receive a “bad substitution” error because this version of the Bash shell doesn’t support this syntax.

So, how can I convert this string into uppercase?

The alternative with this version of Bash is using the tr command:

(localhost)$ DAY=monday
(localhost)$ echo "$DAY" |  tr '[:lower:]' '[:upper:]'
MONDAY

Fix the Bad Substitution Error: A Simple Exercise

Here is a simple exercise to test your knowledge. I have written a script called archive.sh that creates a tar archive of the files in the current directory.

#!/bin/bash

ARCHIVE_NAME=archive.tar.gz
tar cvzf ${$ARCHIVE_NAME} .

if [ $? -eq 0 ]; then
    ARCHIVE_SIZE=${ls -al archive.tar.gz | awk '{print $5}'}
    echo "Archive created successfully. The size of the archive is $ARCHIVE_SIZE"
fi

But it doesn’t work well on purpose:

[myuser@localhost]$ ./archive.sh
./archive.sh: line 4: ${$ARCHIVE_NAME}: bad substitution

Can you see what is causing the error?

Fix the script using what you learned in this tutorial and ensure the error disappears.

Also, look at this guide about the tar command if you want to learn more about it.

Conclusion

In this guide, I have explained the most common causes for the Bash bad substitution error and you now have different ways to understand why this error occurs and to fix it.

This error could be due to:

  • Incorrect ways of using command substitution.
  • Unexpected white spaces within commands.
  • Too many dollar signs when referring to variables.
  • A Bash version that doesn’t support specific features like lowercasing and uppercasing.

Let me know in the comments if the error you are seeing fits one of the scenarios above.

Related FREE Course: Decipher Bash Script

3 thoughts on “[SOLVED] Fix the Bash Bad Substitution Syntax Error”

  1. Do you have a solution for “Fix the Bad Substitution Error: A Simple Exercise” at the end? I am seeing this very issue and can’t find a solution.

    Reply
  2. Hi Melinda

    There are two errors to find in this exercise, on the following lines:
    Error 1: ${$ARCHIVE_NAME}
    Fix: ${ARCHIVE_NAME}

    Error 2: ARCHIVE_SIZE=${ls -al archive.tar.gz | awk ‘{print $5}’}
    Fix: ARCHIVE_SIZE=$(ls -al archive.tar.gz | awk ‘{print $5}’)

    The final script is:

    #!/bin/bash

    ARCHIVE_NAME=archive.tar.gz
    tar cvzf ${ARCHIVE_NAME} .

    if [ $? -eq 0 ]; then
    ARCHIVE_SIZE=$(ls -al archive.tar.gz | awk ‘{print $5}’)
    echo “Archive created successfully. The size of the archive is $ARCHIVE_SIZE”
    fi

    Reply

Leave a Comment