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






Are you using #!/bin/sh or #!/bin/bash from the script?

– Micha Wiedenmann
Mar 2 '13 at 21:10






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.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

How do I collapse sections of code in Visual Studio Code for Windows?

ャフサォクコ ケウ,コ,ワ メ,ロスョノ゙,クネ,フムカヤヲニ,エコ゚ツ ウイオン゙ケワサネォキモュキォウイノンコチ゚メヌナイゥフュ,カヒウネェ ネ,ホノケ,ムュキ ッボーミュハ,チ ツス ィ メウイマヤ,゙ウチ ヅ ロ,ォジヌェ ャヌット ェ,マャ,チナエヒネソキツテ トホヲヲミーァ