我的终端中有一行代码正常工作:
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
,但我得到完全相同的错误。为什么我不能运行这个脚本?
答案 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)
。
最佳做法是两者:
#!/bin/sh
替换为#!/bin/bash
(或您的脚本所依赖的其他shell)。./myscript.sh
或/path/to/myscript.sh
运行此脚本(以及所有其他脚本!),不带前导sh
或bash
。以下是一个例子:
$ 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"
)被认为是一种好习惯。如果存储的文件名包含空格字符,则引用的变量将防止出现问题。$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的章节。