为什么我的bash代码在使用sh运行时失败?
我有一行代码可以在我的终端中正常工作:为什么我的bash代码在使用sh运行时失败?,bash,sh,substitution,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 $
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
$ bash myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3
但是,现在运行时出现错误:
$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution
基于其他问题,我尝试将shebang改为
#/bin/bash
,但我得到了完全相同的错误。为什么我不能运行此脚本?TL;DR:由于您使用的是bash
特定功能,因此您的脚本必须与bash
一起运行,而不是与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
$ bash myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3
看。要了解您使用的是哪种sh:
readlink-f$(哪种sh)
确保特定于bash的脚本始终正确运行的最佳方法
最佳做法包括:
#/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
(相关:)
#的含义/bin/sh
shebang建议系统应该使用哪个shell来运行脚本。这允许您指定#/usr/bin/python
或#/bin/bash
这样您就不必记住哪个脚本是用什么语言编写的
人们使用#/bin/sh
当他们仅使用有限的一组功能(由POSIX标准定义)以实现最大的可移植性时<代码>#/bin/bash对于利用有用的bash扩展的用户脚本来说非常合适
/bin/sh
通常与符合POSIX的最小shell或标准shell(例如bash)进行符号链接。即使在后一种情况下,#/bin/sh
可能会失败,因为bash
将以兼容模式运行,如中所述:
如果用sh名称调用bash,它会尽可能地模仿sh的历史版本的启动行为,同时也符合POSIX标准
sh myscript.sh
只有当您运行/myscript.sh
,/path/to/myscript.sh
,或者当您删除扩展名时,将脚本放在$path
的目录中,然后只运行myscript
时,才会使用shebang
如果显式指定解释器,则将使用该解释器shmyscript.sh
将强制它与sh
一起运行,不管shebang怎么说。这就是为什么仅仅改变shebang是不够的
您应该始终使用首选解释器运行脚本,因此无论何时执行任何脚本,都应首选/myscript.sh
或类似的解释器
对脚本的其他建议更改:
- 引用变量(
而不是“$i”
)被认为是一种良好的做法。如果存储的文件名包含空格字符,则带引号的变量将防止出现问题$i
- 我喜欢你用高级的。我建议使用
(而不是“${I%.mp4}.mp3”
),因为“${I/.mp4/.mp3}”
只在末尾替换(例如名为${parameter%word}
foo.mp4.backup的文件)
${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”
关于这些构造的最终描述是关于的章节。您使用的是#/垃圾箱/垃圾箱或#/bin/bash在控制台中的脚本中,命令由
bash
shell执行。如果您使用的是#/bin/sh
在您的脚本中,它是sh
shell试图执行的,因此出现了错误/第一行中的bin/sh…${foo/bar/baz}不是POSIX,因此可能无法使用特定的/bin/sh。请使用#/而是bin/bash
@MichaWiedenmann应该建议作为一个答案。好的,如果我用bash而不是sh来执行它,它会工作。+1-它还取决于它的执行方式,也就是说,你不能声明为#/bin/bash
然后用sh
调用或声明为#/bin/sh
并使用bash
调用,至少在Debian 7上,您会看到两者都有错误。声明需要与调用匹配。我认为使用#/usr/bin/env bash
会是一个更好的方法。要找出您正在使用的sh:readlink-f$(哪个sh)
@hxysayhi,我将您的评论移到了答案上。如果您能看到任何有助于进一步阅读的内容,请随时更新/改进答案。