I have a line of code that works fine in my terminal:
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done
Then I put the exact same line of code in a script myscript.sh
:
#!/bin/sh for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done
However, now I get an error when running it:
$ sh myscript.sh myscript.sh: 2: myscript.sh: Bad substitution
Based on other questions I tried changing the shebang to #!/bin/bash
, but I get the exact same error. Why can't I run this script?
Using bash command to run the script. We can also use sh to run the script as it will direct to the default shell in the setup environment. Using the sh command to run the bash script. From the above example, we were able to run a bash script using bash as well as the sh command.
Aside from that, if we write a sh script, it will most likely run on Bash without modification because Bash is backward-compatible with sh.
bash and sh are two different shells of the Unix operating system. bash is sh, but with more features and better syntax. Bash is “Bourne Again SHell”, and is an improvement of the sh (original Bourne shell). Shell scripting is scripting in any shell, whereas Bash scripting is scripting specifically for Bash.
TL;DR: Since you are using Bash specific features, your script has to run with Bash and not with sh
:
$ sh myscript.sh myscript.sh: 2: myscript.sh: Bad substitution $ bash myscript.sh ffmpeg -i bar.mp4 bar.mp3 ffmpeg -i foo.mp4 foo.mp3
See Difference between sh and Bash. To find out which sh you are using: readlink -f $(which sh)
.
The best practices are to both:
#!/bin/sh
with #!/bin/bash
(or whichever other shell your script depends on)../myscript.sh
or /path/to/myscript.sh
, without a leading sh
or bash
.Here's an example:
$ cat myscript.sh #!/bin/bash for i in *.mp4 do echo ffmpeg -i "$i" "${i/.mp4/.mp3}" done $ chmod +x myscript.sh # Ensure script is executable $ ./myscript.sh ffmpeg -i bar.mp4 bar.mp3 ffmpeg -i foo.mp4 foo.mp3
(Related: Why ./ in front of scripts?)
#!/bin/sh
The shebang suggests which shell the system should use to run a script. This allows you to specify #!/usr/bin/python
or #!/bin/bash
so that you don't have to remember which script is written in what language.
People use #!/bin/sh
when they only use a limited set of features (defined by the POSIX standard) for maximum portability. #!/bin/bash
is perfectly fine for user scripts that take advantage of useful bash extensions.
/bin/sh
is usually symlinked to either a minimal POSIX compliant shell or to a standard shell (e.g. bash). Even in the latter case, #!/bin/sh
may fail because bash
will run in compatibility mode as explained in the man page:
If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well.
sh myscript.sh
The shebang is only used when you run ./myscript.sh
, /path/to/myscript.sh
, or when you drop the extension, put the script in a directory in your $PATH
, and just run myscript
.
If you explicitly specify an interpreter, that interpreter will be used. sh myscript.sh
will force it to run with sh
, no matter what the shebang says. This is why changing the shebang is not enough by itself.
You should always run the script with its preferred interpreter, so prefer ./myscript.sh
or similar whenever you execute any script.
"$i"
instead of $i
). Quoted variables will prevent problems if the stored file name contains white space characters."${i%.mp4}.mp3"
(instead of "${i/.mp4/.mp3}"
), since ${parameter%word}
only substitutes at the end (for example a file named foo.mp4.backup
).If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With