如果 Bourne shell 在发行版中不可用,在 hashbang 中使用 /bin/sh 是否正确?

Gok*_*tug 15 shell bash ubuntu shell-script shebang

通常,shell 脚本在脚本文件的第一行包含以下注释:#!/bin/sh. 根据我的研究,这被称为“哈希爆炸”,这是常规评论。此注释通知 Unix 该文件由目录下的 Bourne Shell 执行/bin

我的问题从那一点开始。直到现在我还没有看到这样的评论#!/bin/bash。它总是#!/bin/sh。但是,Ubuntu 发行版没有 Bourne Shell 程序。他们有 Bourne Again Shell (bash)。

在这一点上,将注释#!/bin/sh放在用 Ubuntu 发行版编写的 shell 脚本中是否正确?

jes*_*e_b 17

#!/bin/sh应该适用于所有 Unix 和类 Unix 发行版。只要您的脚本保持 POSIX 兼容,它通常被认为是最便携的 hashbang。该/bin/sh外壳应该是一个外壳,实现了POSIX标准的外壳,不管实际的外壳是什么,伪装成/bin/sh壳。


#!/bin/sh现在通常只是一个链接,因为不再维护 Bourne shell。在许多 Unix 系统/bin/sh上将是指向/bin/ksh或的链接/bin/ash,在许多基于 RHEL 的 Linux 系统上它将是一个链接/bin/bash,但是在 Ubuntu 和许多基于 Debian 的系统上它是一个链接/bin/dash。所有 shell 在调用时sh都将进入 POSIX 兼容模式。

hashbang 是一个重要的占位符,因为它比其他方法具有更大的可移植性,只要您的脚本严格符合 POSIX(重复强调重要性)。

注意:当bash在 POSIX 模式下调用时,它仍然允许一些非 POSIX 的东西,比如[[、数组等等。这些事情可能会在非bash系统上失败。

  • @SimonRichter 很高兴看到对此的参考。 (5认同)
  • “在 Ubuntu 上,它是指向 /bin/dash 的链接”以及其他基于 Debian 的系统的链接。FreeBSD/GhostBSD 上的 IIRC 它也是`/bin/ash` 的符号链接。 (3认同)
  • @Kusalananda,嗯,[这个页面](https://www.in-ulm.de/~mascheck/various/shebang/) 说这是一个神话,所以它可能被忽略。 (2认同)

Sté*_*las 12

你已经落后了。/bin/sh这些天来几乎不再是 Bourne shell,而且当您使用#! /bin/shshe-bang时,您会遇到问题¹ 。

Bourne shell 是 70 年代后期编写的 shell,它取代了之前的 Thompson shell(也称为sh)。在 80 年代初,David Korn 为 Bourne shell 编写了一些扩展,修复了一些错误和设计上的尴尬(并引入了一些)并将其称为 Korn shell。

在 90 年代初期,POSIX 指定了基于 Korn shell 子集的sh 语言,大多数¹系统现在已将其更改/bin/sh为 Korn shell 或符合该规范的 shell。在 BSD 的情况下,他们逐渐改变了他们的/bin/sh,最初(在他们由于许可证原因不能再使用 Bourne shell 之后)Almquist shell,Bourne shell 的克隆,带有一些 ksh 扩展,因此它符合 POSIX。

今天,所有 POSIX 系统都有一个 shell sh(大多数情况下,但不一定在/bin. 它通常基于 ksh88、ksh93、pdksh、bash、ash 或 zsh²,但不是 Bourne shell,因为 Bourne shell 从来不符合 POSIX 标准³。几个这些炮弹(bashzshyash和一些pdksh衍生物时作为调用使POSIX兼容模式sh,且较少柔顺以其他方式)。

bash(GNU 对 Korn shell 的回答)实际上是唯一被认证为符合 POSIX sh(作为 macOS 认证的一部分)。

当您使用#! /bin/sh -she-bang编写脚本时,您应该使用标准sh语法(如果您想要可移植,还应该使用该脚本中使用的实用程序的标准语法,在解释 shell 时不仅涉及 shell脚本),那么sh使用该标准语法解释器的什么实现都无关紧要(kshbash...)。

只要您不使用这些 shell,这些 shell 是否对标准进行了扩展都没有关系。这就像编写 C 代码一样,只要您编写标准 C 代码并且不使用一个编译器(如gcc)或另一个编译器的扩展,您的代码应该可以正常编译,无论编译器实现如何,只要编译器兼容。

在这里,你的#! /bin/sh她爆炸,你的主要问题将是其中的系统/bin/sh是Bourne shell的,对于实例不支持标准功能,如$((1+1))$(cmd)${var#pattern}...在你可能需要这种情况下,像变通办法:

#! /bin/sh -
if false ^ true; then
  # in the Bourne shell, ^ is an alias for |, so false ^ true returns
  # true in the Bourne shell and the Bourne shell only.
  # Assume the system is Solaris 10 (older versions are no longer maintained)
  # You would need to adapt if you need to support some other system
  # where /bin/sh is the Bourne shell.
  # We also need to fix $PATH as the other utilities in /bin are
  # also ancient and not POSIX compatible.
  PATH=`/usr/xpg6/bin/getconf PATH`${PATH:+:}$PATH || exit
  export PATH
  exec /usr/xpg4/bin/sh "$0" "$@"
  # that should give us an environment that is mostly  compliant
  # to the Single UNIX Specification version 3 (POSIX.2004), the
  # latest supported by Solaris 10.
fi
# rest of the script
Run Code Online (Sandbox Code Playgroud)

顺便说一下,Ubuntu/bin/sh不是bash默认的。现在dash,一个基于 NetBSD 的外壳sh,本身基于 Almquist 外壳,除了不支持多字节字符外,它主要符合 POSIX 标准。在Ubuntu和其他基于Debian的系统,你可以选择bashdash/bin/shdpkg-reconfigure dash4shDebian 附带的脚本在两个 shell 中的工作方式应该相同,因为它们是按照 Debian 策略标准(POSIX 标准的超集)编写的。您可能会发现他们还工作确定zshsh仿真或bosh(可能不是ksh93,也不yash该没有local内置(由Debian政策而不是POSIX)要求)。

unix.stackexchange.com 范围内的所有系统在sh 某处都有一个 POSIX 。他们中的大多数都有一个/bin/sh(您可能会发现非常罕见的没有/bin目录但您可能并不关心它),并且通常是一个 POSIXsh解释器(在极少数情况下是一个(非标准)Bourne shell )。

但它sh是您可以确保在系统上找到的唯一 shell 解释器可执行文件。对于其他 shell,您可以确定 macOS、Cygwin 和大多数 GNU/Linux 发行版都有bash. SYSV 派生的操作系统(Solaris、AIX...)通常具有 ksh88,可能是 ksh93。OpenBSD、MirOS 都会有 pdksh 的衍生品。macOS 将具有zsh. 但除此之外,将无法保证。不保证是否bash或任何其他 shell 将安装在/bin或其他地方(/usr/local/bin例如,安装时通常会在BSD 上找到)。当然也不保证将安装的 shell 的版本。

请注意,这#! /path/to/executable不是约定,它是所有类 Unix 内核(由 Dennis Ritchie 在 80 年代早期引入)的一个特性,它允许通过在以 开头的第一行中指定解释器的路径来执行任意文件#!。它可以是任何可执行文件。

当您执行第一行以 开头的文件时#! /some/file some-optional-arg,内核最终/some/filesome-optional-arg、脚本路径和作为参数的原始参数执行。您可以创建第一行#! /bin/echo test以查看发生了什么:

$ ./myscript foo
test ./myscript foo
Run Code Online (Sandbox Code Playgroud)

当您使用/bin/sh -代替 时/bin/echo test,内核会执行/bin/sh - ./myscript foosh解释存储在其中的内容代码myscript并忽略第一行,因为它是注释(以 开头#)。


¹ 可能今天我们中任何人都会遇到/bin/sh基于 Bourne shell的唯一系统是 Solaris 10。Solaris 是少数几个决定保留 Bourne shell 以实现向后兼容性的 Unices 之一(POSIXsh语言并不完全向后兼容的Bourne shell兼容)和(至少在台式机和全服务器部署)有POSIXsh其他地方(在/usr/xpg4/bin/sh基于ksh88),但改变了Solaris 11的地方/bin/sh,现在是ksh93的。其他的大多已不复存在。

²所述/bin/sh的MacOS / X曾经是zsh,但后来改变到bashzsh用作 POSIXsh实现并不是主要关注点。它的sh模式主要是能够在脚本中嵌入或调用 ( source) POSIXsh代码zsh

³ 最近,@schily扩展了 OpenSolaris shell(基于 SVR4 shell,基于 Bourne shell)以符合 POSIX 标准,被称为bosh但我不知道它已在任何系统上使用。除此之外ksh88,它是基于 Bourne shell 代码的第二个符合 POSIX 的 shell

4在旧版本中,您还可以使用mksh或其更多的 POSIXlksh化身。那是基于 pdksh 的 MirOS(以前的 MirBSD)shell,它本身基于 Forsyth shell(Bourne shell 的另一个重新实现))


thr*_*rig 8

是的,您可以#!/bin/sh在脚本中使用,因为/bin/sh(希望)在此类系统上提供,通常通过某种链接使bash行为(或多或少)像一个sh会。例如,这是一个 Centos7 系统,它链接shbash

-bash-4.2$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec  4 16:48 /bin/sh -> bash
-bash-4.2$ 
Run Code Online (Sandbox Code Playgroud)

#!/bin/bash如果您bash仅为该系统编写脚本并想要使用bash功能,您也可以使用。然而,这样的脚本会受到可移植性问题的影响,例如在 OpenBSD 上,bash只有在管理员不厌其烦地安装它(我没有)然后将它安装到/usr/local/bin/bash,而不是/bin/bash. 严格的 POSIX#!/bin/sh脚本应该更具可移植性。

  • 当我在工作中为 RHEL 服务器编写脚本时,我精确地使用了 `#!/bin/bash`,以便我可以利用非 POSIX 功能。 (2认同)

Sto*_*bor 6

你问

将注释 #!/bin/sh 放在以 ubuntu 发行版编写的 shell 脚本中是否正确?

答案取决于您在 shell 脚本中编写的内容。

  • 如果您严格使用可移植的 POSIX 兼容脚本,并且不使用任何特定于 bash 的命令,那么您可以使用/bin/sh.

  • 如果您知道您只在装有 bash 的机器上使用该脚本,并且您想使用特定于 bash 的语法,那么您应该使用/bin/bash

  • 如果您想确保脚本可以在各种 unix 机器上运行,那么您应该只使用符合 POSIX 的语法,并且/bin/sh

  • 如果您经常使用另一个 shell(例如kshzshtcsh),并且想在您的脚本中使用该语法,那么您应该使用适当的解释器(例如/bin/ksh93, /bin/zsh, 或/bin/tcsh