当我用sh运行它时,为什么我的bash代码会失败?

问题描述 投票:34回答:2

我有一行代码可以在我的终端中正常工作:

for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done

然后我将完全相同的代码行放在脚本myscript.sh中:

#!/bin/sh
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done

但是,现在运行时出现错误:

$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution

基于其他问题,我尝试将shebang更改为#!/bin/bash,但我得到完全相同的错误。为什么我不能运行这个脚本?

bash sh substitution
2个回答
60
投票

TL; DR:由于您使用的是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

Difference between sh and bash。要找出你正在使用的sh:readlink -f $(which sh)

The best way to ensure a bash specific script always runs correctly

最佳做法是:

  1. #!/bin/sh(或者你的脚本所依赖的其他shell)替换#!/bin/bash
  2. 使用./myscript.sh/path/to/myscript.sh运行此脚本(以及所有其他脚本!),没有领先的shbash

这是一个例子:

$ 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

(相关:Why ./ in front of scripts?

The meaning of #!/bin/sh

shebang建议系统应该使用哪个shell来运行脚本。这允许您指定#!/usr/bin/python#!/bin/bash,这样您就不必记住哪种脚本是用哪种语言编写的。

人们使用#!/bin/sh时只使用一组有限的功能(由POSIX标准定义)以获得最大的可移植性。 #!/bin/bash非常适合利用有用的bash扩展的用户脚本。

/bin/sh通常符号链接到最小的POSIX兼容shell或标准shell(例如bash)。即使在后一种情况下,#!/bin/sh可能会失败,因为bash将以兼容模式运行,如manpage所述:

如果使用名称sh调用bash,它会尝试尽可能接近地模仿sh的历史版本的启动行为,同时也符合POSIX标准。

The meaning of sh myscript.sh

shebang仅在你运行./myscript.sh/path/to/myscript.sh时使用,或者当你删除扩展名时,将脚本放在$PATH的目录中,然后运行myscript

如果明确指定解释器,则将使用该解释器。无论shebang说什么,sh myscript.sh都会强迫它与sh一起运行。这就是为什么改变shebang本身是不够的。

您应该始终使用其首选解释器运行脚本,因此每当您执行任何脚本时,更喜欢./myscript.sh或类似的脚本。

Other suggested changes to your script:

  • 引用变量("$i"而不是$i)被认为是一种好习惯。如果存储的文件名包含空格字符,则引用的变量将防止出现问题。
  • 我喜欢你使用先进的parameter expansion。我建议使用"${i%.mp4}.mp3"(而不是"${i/.mp4/.mp3}"),因为${parameter%word}只在末尾替换(例如一个名为foo.mp4.backup的文件)。

10
投票

${var/x/y/}构造不是POSIX。在您的情况下,只需删除变量末尾的字符串并粘贴另一个字符串,便携式POSIX解决方案即可使用

#!/bin/sh
for i in *.mp4; do 
    ffmpeg -i "$i" "${i%.mp4}.mp3"
done

甚至更短,ffmpeg -i "$i" "${i%4}3"

关于这些结构的权威性原则是关于Parameter Expansion for the POSIX shell的章节。

© www.soinside.com 2019 - 2024. All rights reserved.