我读过这样的文章,如果想以可移植的方式使用Bash,则应使用shebang:
#!/usr/bin/env bash
但是现在我想知道:当我想明确说明我不依赖Bash,而是编写了遵循POSIX标准的脚本时,我应该使用:
#!/bin/sh
还是在这里也最好使用#!/usr/bin/env sh
?
POSIX规范sh:应用程序使用的信息部分指出,您不能依赖于sh
可执行文件安装在/bin/sh
路径下。
应用程序应注意标准shell的PATH不能假定为/bin/sh或/usr/bin/sh,而应通过查询由getconf返回的PATH来确定,确保返回的路径名为绝对路径,而不是shell内置。
例如,要确定标准sh实用程序的位置:
然而,它建议在安装时修改shell脚本以使用完整的command -v sh
sh
路径,而不是建议使用env
来使用适当的PATH:
此外,在支持可执行脚本的系统上(“#!”结构),建议应用程序使用getconf的PATH确定shell路径名并在安装时相应地更新“#!”脚本(例如,使用sed)。
实践中,我主要编写POSIX shell脚本,实际上每个GNU/Linux系统(Red Hat和Debian-based)-和其他如Cygwin和OS X - 都有符合POSIX标准的sh
,安装到/bin/sh
或可用作该路径的软链接或硬链接。我从未需要使用env
来适应不使用此路径的sh
的系统。
可能存在一些Unix系统,其中没有作为/bin/sh
可用的符合POSIX标准的sh
。 POSIX规范建议在某些系统上安装为/usr/xpg4/bin/sh
。据我所知,这对于Solaris系统是真实(过去?),其中/bin/sh
是早期版本的Bourne shell,它在POSIX之前发布。在这种情况下,使用env sh
不能保证有所帮助,因为它仍然可能在/usr/xpg4/bin/sh
上找到POSIX shell之前在/bin/sh
处找到Bourne shell。
概括起来,如果您要为通用Unix和Linux操作系统编写POSIX shell脚本,请使用#!/bin/sh
作为shebang。
在极少数情况下,/bin/sh
是Bourne shell而不是符合POSIX标准的shell,则必须修改shebang以使用适当的完整路径到POSIX shell。
在任何情况下,使用#!/usr/bin/env sh
没有好处,与其使用#!/bin/sh
相比可能更容易失败。
/usr/xpg4/bin
出现在/usr
之前吗? - tripleeepython
的shebang(惯例上为#!python
作为最小示例)为正确和适当的安装程序指定路径。(这处理了“非标准”路径和Python 2和Python 3之间的差异,两者都可以本地安装。) - chepner我会说 #!/usr/bin/env sh
更可取。
一些可移植的构造,例如循环遍历find
的结果, 需要显式调用 sh
。请考虑此脚本:
#!/bin/sh
# ... some commands here ...
find . -exec sh -c '
for file do
# ... commands processing "$file" ...
done' find-sh {} +
开头的命令将由/bin/sh
运行,而处理"$file"
的命令将由PATH中第一个出现的sh
运行,这可能与/bin/sh
的行为不同。这是意外错误的潜在来源。 #!/usr/bin/env sh
shebang解决了这个问题,因为所有命令都将由PATH中第一个sh
运行。
#!/usr/bin/env sh
shebang唯一的潜在缺点是在调用脚本时/usr
可能没有挂载。然而,在实践中,这种情况不应经常发生。在可移植脚本中经常使用的外部程序,例如awk
,也经常在/usr/bin
中找到,因此很难确保脚本在未挂载/usr
的情况下正确运行。
如果您真的想要可移植性并且不依赖于/usr
被挂载,可以按以下方式开始脚本,以确保它始终由PATH中的sh
执行:
#!/bin/sh
if test X"$SUBSHELL" != X"1"; then
SUBSHELL=1
export SUBSHELL
exec sh "$0" "$@"
exit 1
fi
# ... your actual script comes here ...
但这似乎有点过度,所以我认为#!/usr/bin/env sh
shebang是一个合理的折衷方案。
bash
,它也扩展了可移植的定义。它只是说“使用您路径中首先命名为bash
的程序”。它没有说明该版本的bash
应该是什么(3.2?4.0?4.1?4.2?4.3?4.4?5.0?),甚至没有说命名为bash
的程序是否真的是Bash。记录运行脚本所需的内容;让用户担心如何指定它。 - chepner