Why does my bash code fail when I run it with sh?
Why does my bash code fail when I run it with sh?
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:
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?
#!/bin/bash
In console, the commands were executed by
bash shell. If you are using #!/bin/sh in your script, it is sh shell trying to execute and hence the error.– jitendra
Mar 2 '13 at 21:16
bash
#!/bin/sh
sh
I wrote #!/bin/sh in the first line...
– WhatIsName
Mar 2 '13 at 21:18
$foo/bar/baz is not POSIX, and therefore may not work with your particular /bin/sh. Use
#!/bin/bash instead. @MichaWiedenmann should suggest that as an answer.– that other guy
Mar 2 '13 at 21:19
#!/bin/bash
OK, if i execute it with bash instead of sh it works.
– WhatIsName
Mar 2 '13 at 21:21
2 Answers
2
TL;DR: Since you are using bash specific features, your script has to run with bash and not with sh:
bash
bash
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
bash is basically to sh what C++ is to C. See Difference between sh and bash.
bash
sh
The best practices are to both:
#!/bin/sh
#!/bin/bash
./myscript.sh
/path/to/myscript.sh
sh
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.
#!/usr/bin/python
#!/bin/bash
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
#!/bin/bash
/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 wil run in compatibility mode as explained in the manpage:
/bin/sh
#!/bin/sh
bash
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.
./myscript.sh
/path/to/myscript.sh
$PATH
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.
sh myscript.sh
sh
You should always run the script with its preferred interpreter, so prefer ./myscript.sh or similar whenever you execute any script.
./myscript.sh
"$i"
$i
"$i%.mp4.mp3"
"$i/.mp4/.mp3"
$parameter%word
foo.mp4.backup
+1 - It also depends on the way it is executed, I.E. You can't declare as
#!/bin/bash then invoke with sh Or declare as #!/bin/sh and invoke with bash, at least on Debian 7 you will see a error on both. The declaration needs to match the invocation.– webLacky3rdClass
Dec 13 '13 at 19:03
#!/bin/bash
sh
#!/bin/sh
bash
I think using
#!/usr/bin/env bash would be a better way.– Hozefa
Sep 11 '16 at 0:22
#!/usr/bin/env bash
@AaronFranke, how are you running the script? If you run it with
sh yourscript, then the #!/bin/bash makes no difference.– Charles Duffy
Jan 18 '17 at 16:31
sh yourscript
#!/bin/bash
./myscript.sh– Aaron Franke
Jan 18 '17 at 21:44
./myscript.sh
The $var/x/y/ construct is not POSIX. In Your case, where you just remove a string at the end of a variable and tack on another string, the portable POSIX solution is to use
$var/x/y/
#!/bin/sh
for i in *.mp4; do
ffmpeg -i "$i" "$i%.mp4.mp3"
done
or even shorter, ffmpeg -i "$i" "$i%43".
ffmpeg -i "$i" "$i%43"
The definitive dope for these constructs is the chapter on Parameter Expansion for the POSIX shell.
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Are you using #!/bin/sh or #!/bin/bash from the script?
– Micha Wiedenmann
Mar 2 '13 at 21:10