A Mistake I’ve Been Making for Years About .sh Scripts
Adam C. |

For a long time, I’ve been naming my shell scripts with the .sh extension and assuming they’d run just fine in any shell. Most of the time, they worked perfectly—but every now and then, I’d hit unexpected issues, especially when running them with sh scriptName.sh.

This got me thinking: Why do we even name our scripts scriptName.sh? Shouldn't it be scriptName.bash if it's written specifically for Bash?

Photo by Lauren on Unsplash

The Shebang Problem

Most of my scripts begin with a shebang like this:

#!/bin/bash

This line ensures the script is executed with Bash when run directly, for example:

./scriptName.sh

However, if I run the same script with:

sh scriptName.sh

The shebang is completely ignored! Instead, the script is executed using sh (which might point to a more basic shell like dash on many systems). This often works fine—until the script relies on Bash-specific features.

What Happens When sh Runs a Bash Script?

Here’s an example of a script that breaks when run with sh:

#!/bin/bash
echo "Using Bash!"
my_array=(one two three)
echo "Second element: ${my_array[1]}"

Run with Bash (or Directly):

./scriptName.sh

Output:

Using Bash!
Second element: two

Run with sh:

sh scriptName.sh

Output:

Using Bash!
scriptName.sh: 3: scriptName.sh: Syntax error: "(" unexpected

The error occurs because sh doesn’t support arrays or other Bash-specific features. This happens because the sh command forces the use of a simpler shell, completely ignoring the shebang.

Why .sh Can Be Misleading

Naming a script scriptName.sh implies it’s compatible with sh (or any POSIX-compliant shell). But if your script relies on Bash-specific features, this isn’t true. This mislabeling can confuse others—or even you—into running the script incorrectly.

A Case for .bash

If a script depends on Bash, naming it with the .bash extension might be more accurate. For example:

scriptName.bash

This naming convention immediately signals that the script is Bash-specific, reducing the likelihood of running it with sh by mistake.

What I’ve Learned (and What I’ll Do Differently)

  1. Be Explicit with the Interpreter:

If my script starts with #!/bin/bash, I’ll always run it using Bash:

bash scriptName.sh
  • If a script is POSIX-compliant, I’ll test it with sh to ensure compatibility.
  1. Use the Shebang Correctly:

Make scripts executable and run them directly:

chmod +x scriptName.sh
./scriptName.sh
  1. Adopt Clear File Extensions:
    • Use .sh only for POSIX-compliant scripts.
    • Use .bash for scripts that rely on Bash-specific features.
  2. Write Scripts with Compatibility in Mind:
    • If I expect the script to be run with sh, I’ll avoid Bash-only features like arrays or [[ ]].
    • If it’s written for Bash, I’ll clearly document it and name it appropriately.

Moving Forward

Understanding the difference between sh and bash and how the shebang works has saved me a lot of frustration. Going forward, I’ll pay closer attention to how I name and run my scripts to ensure compatibility and clarity.

What about you? Have you ever run into similar issues with .sh scripts or interpreter mismatches? Let me know your thoughts—I’d love to hear your experiences!