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

时间:2013-03-02 21:06:59

标签: bash sh substitution

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

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,但我得到完全相同的错误。为什么我不能运行这个脚本?

2 个答案:

答案 0 :(得分: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。要找出您正在使用的内容:readlink -f $(which sh)

确保bash特定脚本始终正确运行的最佳方法

最佳做法是两者

  1. #!/bin/sh替换为#!/bin/bash(或您的脚本所依赖的其他shell)。
  2. 使用./myscript.sh/path/to/myscript.sh运行此脚本(以及所有其他脚本!),不带前导shbash
  3. 以下是一个例子:

    $ 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?

    #!/bin/sh

    的含义

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

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

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

      

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

    sh myscript.sh

    的含义

    只有在您运行./myscript.sh/path/to/myscript.sh或删除扩展程序时才会使用shebang,将脚本放在$PATH的目录中,然后运行{{1 }}

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

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

    您的脚本的其他建议更改:

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

答案 1 :(得分: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的章节。