Script doesn't work when it is run using '#!/bin/sh'

Solution 1:

Your friend on another computer probably uses an OS which has /bin/sh linked to /bin/bash. In Ubuntu (actually, Debian and most Debian derivatives), /bin/sh is not linked to /bin/bash, but to /bin/dash, which doesn't support many bash-specific features, but is considerably faster.

On Arch Linux:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Sep 28 15:26 /bin/sh -> bash

On Ubuntu:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Feb 19  2014 /bin/sh -> dash

The right thing to do

If you use a shebang, mean it. Since your script contains bash-isms, use /bin/bash in the shebang. Or write portable, POSIX-compliant code.

You can speed up this process by using the checkbashisms program mentioned in this LWN article. It's part of the devscripts package, so install it first:

sudo apt-get install devscripts

Thus:

checkbashisms /path/to/script.sh || sed -i '1 s;/bin/sh;/bin/bash;' /path/to/script.sh

You can convert this to a script (say convert.sh):

#! /bin/sh

for i
do
    checkbashisms "$i"
    if [ $? = "1" ]
    then
        sed -i '1 s;/bin/sh;/bin/bash;' "$i"
    fi
done

The specific return code of 1 means that checkbashisms found a possible bashism, and other return values indicate other problems (file not readable, missing shebang, etc.), so we can check for that particular return value.

And then call it with:

./convert.sh /path/to/first/script.sh /path/to/second/script.sh
# or 
./convert.sh *.sh
# or
find . -iname '*.sh' -exec ./convert.sh {} +

The wrong thing to do

Replace /bin/sh with a symbolic link to /bin/bash.

Recommended reading:

  • Bashism - compares bash and dash syntax and shows changes needed.
  • Dash as /bin/sh - similar.