什么是首选的Bash shebang(“#!”)?

1546

大多数情况下,有没有一种Bash shebang比其他shebang更为客观地优秀?

  • #!/usr/bin/env bash
  • #!/bin/bash
  • #!/bin/sh
  • #!/bin/sh -
  • 等等

我模糊地记得很久以前听说在结尾添加一个破折号可以防止有人向您的脚本传递命令,但是找不到任何详细信息。


12
在OpenBSD上,它是/usr/local/bin/bash - jww
5
添加破折号的目的是为了防止某种类型的setuid root欺骗攻击,详情请参见https://security.stackexchange.com/questions/45490/what-is-setuid-based-script-root-spoofing?newreg=e1ab392777f44bd8bf57e33def375cf4 - Vladislav Ivanishin
8
我想给这个点赞,但它已经得了1337分,我不想打扰它! - Thomas G Henry
2
当一个suid程序执行具有这样的shebang的bash脚本时,#!/usr/bin/env bash会带来特权升级安全威胁。用户可以简单地操纵他的PATH,并获取任意bash可执行文件以代替运行,从而获得提升的权限。 - Eric
偶然发现一个相关的问题:https://dev59.com/0GEi5IYBdhLWcg3wIJJH - Jens
请注意,答案非常依赖于您的脚本是否实际需要成为* bash *脚本(依赖于bash扩展),或者任何(POSIX)sh都可以。 - mtraceur
7个回答

1991

为了可移植性,应使用#!/usr/bin/env bash:不同的*nix将bash放置在不同的位置,并使用/usr/bin/env是运行在PATH上找到的第一个bash的解决方法。

而且sh不是bash


12
谢谢。另外,在$!/usr/bin/env bash后加上“-”似乎没有任何作用,因为*nix中的shebang只允许一个参数,并且被“bash”使用。这显然只有在脚本的shebang是其中之一并且没有参数(例如“/bin/sh”)时,才有用于防止恶意参数传递到脚本的命令行。 - Kurtosis
16
在某些系统中,bash 不会存储在 /bin 中。 - ptierno
21
和我一样,我只是将它添加到了一个别名中:alias shebang='echo "#!/usr/bin/env bash"',现在我只需要打开终端并输入shebang,而不必再到这里去。 - Oylex
32
这个答案是有误导性的。POSIX并没有说 env 要放在 /usr/bin/env 这个路径下。实际上,只要它在 PATH 中可见即可,可能在任何地方,比如 /bin/env 或者 /dummy/env (如果 /dummy 在 PATH 中的话)。在 POSIX 中,shebang 本身没有定义,所以我可以让 #!stop toaster 来启动 USB 咖啡机并且依旧符合 POSIX 标准。因此,#!/usr/bin/env bash 并不比 #!/bin/bash 更好,具体取决于移植性。 - darkfeline
42
可移植性并非绝对——在每个平台上都能执行相同操作的脚本是数学上不可能的。自2012年至2018年,/usr/bin/env 的存在比 /bin/bash/usr/bin/bash 中的任何一个都更常见,因此以这行代码开头的脚本将在尽可能多的机器上按预期运行 - l0b0
显示剩余17条评论

232
在大多数但不是所有的系统上,我建议使用以下方法:
#!/bin/bash

这并不是100%可移植的(有些系统将bash放在除了/bin之外的位置),但事实上,许多现有脚本使用#!/bin/bash,迫使各种操作系统将/bin/bash至少设置为主要位置的符号链接。

另一种选择是:

#!/usr/bin/env bash

有人建议过 - 但不能保证env命令在/usr/bin中(我曾经使用过没有的系统)。此外,这种形式将使用当前用户$PATH中的第一个bash实例,这可能不是适合的Bash shell版本。

(但是/usr/bin/env应该在任何相当现代的系统上工作,要么是因为env/usr/bin中,要么是因为系统做了一些使其工作的事情。我上面提到的系统是SunOS 4,我大约25年没用过了。)

如果您需要在没有/bin/bash的系统上运行脚本,可以修改脚本以指向正确的位置(这确实不方便)。

我在我的回答这个问题中更深入地讨论了权衡。

一个有点隐晦的更新:我使用的一个系统,Termux,是在Android下运行的类似桌面Linux的层,它没有/bin/bash(bash是/data/data/com.termux/files/usr/bin/bash),但它有特殊处理来支持#!/bin/bash。
更新:正如Edward L.在评论中指出的那样,bash不是FreeBSD上的“基本操作系统”的一部分,即使默认安装了它,它也可能不会安装为/bin/bash。在这样的系统上,您可以使用#!/usr/bin/env技巧(我假设FreeBSD将env安装为/usr/bin/env),或者您可以使用bash安装的路径(显然是#!/usr/local/bin/bash)。如果您的脚本只打算在FreeBSD下运行,您可以使用#!/usr/local/bin/bash。如果它们是可移植的,您可以使用#!/usr/bin/env技巧(它有一些缺点;请参阅我在上述引用中的答案)或者在安装脚本时更新#!行。
可能在其他一些操作系统上也存在类似的问题。

16
两年过去了,这仍然是这里最好的建议。如果简单的解决方案行不通,那就必须质疑你之前的决定。被接受并得到最多赞的答案并没有错,只是不完全正确 :) - Software Engineer
Termux 对 #!/usr/bin/env 也有特殊处理,对吗? - mtraceur
@mtraceur 是的,它确实可以 - 而且我刚刚发现这个特殊处理在命令行上也适用,不仅仅是在 #! 行上。 - Keith Thompson

94

/bin/sh 通常是系统默认shell的链接,通常是 bash,但在 Debian 系统上是更轻量级的 dash。无论如何,原始 Bourne shell 是 sh,因此如果脚本使用一些 bash(第二代 "Bourne Again sh")特定功能([[ ]]测试、数组、各种糖果特性等),则应更具体地使用后者。这样,在未安装 bash 的系统上,你的脚本就不会运行。虽然我知道可能有一部关于这个演变的精彩三部曲电影......但那只是谣言。

此外,请注意当作为 sh 调用时,bash 在某种程度上会表现为 POSIX 标准的 sh(也请参见 GNU 文档 关于此的说明)。


3
OpenBSD默认使用公共领域Korn Shell(pdksh)。 - jww
2
大多数系统 不会/bin/sh 链接到 /usr 中的任何位置,因为这样会使 init 脚本在 /usr 挂载之前运行变得相当困难。 - aij
1
@aij 我不知道为什么我在那里加了“许多或大多数” - 我是一个 Fedora 用户,多年来 /bin/sbin 默认情况下只是符号链接到 /usr/bin/usr/sbin,所以在这种情况下 /bin/sh 是一个指向 bash 的链接,实际目录是 /usr/bin。但我会更正上面的内容。 - CodeClown42

37

使用shebang行调用适当的解释器不仅适用于BASH。您可以在系统上使用任何解释语言的shebang,例如Perl,Python,PHP(CLI)和许多其他语言。顺便说一下,shebang

#!/bin/sh -

如果是两个破折号(--),则选项结束后的所有内容都将被视为文件名和参数。

使用 env 命令可以使您的脚本具有可移植性,并允许您为脚本设置自定义环境,因此可移植脚本应使用该命令。

#!/usr/bin/env bash

无论是哪种语言,比如Perl

#!/usr/bin/env perl

一定要查看 bashman 页面:

man bash

env

man env

注意:在 Debian 和基于 Debian 的系统(如 Ubuntu)中,shbash 不同,而是链接到 dash。由于所有系统脚本都使用 sh,因此 Debian 认为这样做可以使 bash 发展,并使系统保持稳定。

此外,为了保持*nix调用的方式,我从不在 shebang 调用的脚本中使用文件扩展名,因为你不能像在 Windows 上那样省略可执行文件的扩展名来调用它。文件命令可以将其识别为脚本。


6

这取决于你如何编写bash脚本。如果你的/bin/sh是符号链接到bash,那么当bash作为sh调用时,一些功能将不可用

如果你想使用bash特定的非POSIX功能,请使用#!/bin/bash


4
在OpenBSD上未安装Bash。如果通过pkg_add安装它,则位于/usr/local/bin中,该路径可能不在环境变量$PATH中。 - jww
1
POSIX 功能怎么样? - Nikolan Asad

2
如果可移植性包括“在alpine linux上运行”,而没有安装bash,则使用#!/bin/sh,不要明确指定bash。并且将脚本保持为POSIX兼容,而不是依赖于bash扩展。现在运行alpine linux的地方数量远远超过那些#!/bin/sh无法工作的稀有奇怪系统的数量。
在ubuntu和alpine上,将会运行一些ash的变体。这也意味着如果你在bash之上运行,你受到与bash相关的安全漏洞(如shellshock)的影响较小。Bash被广泛使用,但它的内部实现很糟糕,明确指定它作为Shell的使用方式而不依赖系统选择的Shell是不好的做法。

0

#!/bin/sh

大多数脚本不需要特定的bash功能,应该编写为sh。

此外,这使得脚本可以在默认情况下没有bash的BSD上工作。


7
但问题是用什么来特别执行 Bash 脚本。这有一个明显且可能严重的缺点,即它无法用于 Bash 脚本(即使用仅限于 Bash 的功能的任何内容)。这是新手常犯的一个陷阱。另请参见shbash之间的区别 - tripleee
提问者特别询问“/bin/sh”是否适合bash脚本,因此我认为weberjn的回答并不超出范围。 - Eric
@Eric 是的,但正如 @tripleee 所说,它不是 Bash 脚本的好选择,因为原始 Bourne shell 缺少 Bash 添加的功能。 - BadHorsie
这些功能在没有Bash的系统上不可用,包括BSDs,以及基于busybox的任何系统,这是运行容器中大部分内容的原因,无论是容器大小还是安全性方面。 - saolof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接