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

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

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

我的问题就是从那时开始的。到目前为止我还没有看到这样的评论#!/bin/bash。总是如此#!/bin/sh。然而,Ubuntu 发行版没有 Bourne Shell 程序。他们有 Bourne Again Shell (bash)。

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

答案1

#!/bin/sh应该适用于所有 Unix 和类 Unix 发行版。只要您的脚本保持 POSIX 兼容,它通常被认为是最可移植的 hashbang。 shell/bin/sh应该是实现 POSIX shell 标准的 shell,无论伪装成/bin/shshell 的实际 shell 是什么。


#!/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系统上失败。

答案2

你已经把它搞反了。/bin/sh现在几乎不再是 Bourne shell,只有当你使用#! /bin/shshe-bang 时才会遇到问题。

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

20 世纪 90 年代初,POSIX 指定了sh 语言基于 Korn shell 的子集,大多数系统现在已更改/bin/sh为 Korn shell 或符合该规范的 shell。就 BSD 而言,他们/bin/sh最初(由于许可证原因而无法再使用 Bourne shell)逐渐更改了 Almquist shell,它是 Bourne shell 的克隆,带有一些 ksh 扩展,因此它符合 POSIX 标准。

如今,所有 POSIX 系统都有一个名为 的 shell sh(最常见,但不一定在 中/bin,POSIX 不指定它指定的实用程序的路径),该 shell 大部分与 POSIX 兼容。它通常基于 ksh88、ksh93、pdksh、bash、ash 或 zsh²,但不是基于 Bourne shell,因为 Bourne shell 从来不兼容 POSIX。其中一些 shell(、bash和一些派生程序在被调用为和时启用 POSIX 兼容模式zshyashpdkshsh较少的否则符合规定)。

bash(Korn shell 的 GNU 答案)实际上是唯一一个被认证为开源 shell(可以说目前仅在维护中,因为其他 shell 通常基于 ksh88,而 ksh88 自 90 年代以来就没有获得任何新功能)符合 POSIX 标准sh(作为 macOS 认证的一部分)。

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

只要您不使用这些 shell,它们是否具有超出标准的扩展并不重要。就像编写 C 代码一样,只要您编写标准 C 代码并且不使用一种编译器(例如gcc)或另一种编译器的扩展,无论编译器实现如何(只要编译器兼容),您的代码都应该可以正常编译。

在这里,对于你的#! /bin/shshe-bang,你的主要问题将是 Bourne shell 所在的系统,/bin/sh例如不支持标准功能,如$((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

顺便说一下,Ubuntu/bin/sh不是bash默认的。如今dash,一个基于 NetBSD 的 shellsh本身基于 Almquist shell,该 shell 主要兼容 POSIX,只是它不支持多字节字符。在 Ubuntu 和其他基于 Debian 的系统上,您可以在bashdashfor/bin/sh之间进行选择dpkg-reconfigure dash) 4shDebian 附带的脚本在两个 shell 中的工作方式应该相同,因为它们是根据 Debian 策略标准(POSIX 标准的超集)编写的。您可能会发现它们在zshsh仿真中也可以正常工作bosh(可能没有内置功能ksh93,也yash没有local内置功能(Debian 政策要求,但 POSIX 不要求))。

unix.stackexchange.com 范围内的所有系统在某处都有 POSIX sh 。他们中的大多数都有一个/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通常会在 BSD 上找到)。/usr/local/bin当然,不能保证将安装的 shell 的版本。

请注意,这#! /path/to/executable不是习俗,这是所有类 Unix 内核的一个特性(80 年代初由丹尼斯·里奇 (Dennis Ritchie) 推出),允许通过在以 开头的第一行中指定解释器的路径来执行任意文件#!。它可以是任何可执行文件。

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

$ ./myscript foo
test ./myscript foo

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


当今,我们中的任何人可能会遇到的唯一一个/bin/sh基于 Bourne shell 的系统是 Solaris 10。Solaris 是为数不多的决定保留 Bourne shell 以实现向后兼容性的 Unices 之一(POSIXsh语言尚未完全实现)向后兼容 Bourne shell)并且(至少对于桌面和完整服务器部署)sh在其他地方(/usr/xpg4/bin/sh基于 ksh88)具有 POSIX,但在 Solaris 11 中发生了变化,/bin/sh现在是 ksh93。其他的大部分都已经失效了。

²MacOS/X 的曾经/bin/shzsh,但后来改为bash。它的主要焦点不是zsh用作 POSIXsh实现。其sh模式主要是能够在脚本中嵌入或调用( source)POSIXsh代码zsh

近期,@希利扩展了 OpenSolaris shell(基于 SVR4 shell、基于 Bourne shell)以兼容 POSIX,称为bosh但我还不知道它已在任何系统上使用。除此之外ksh88,它还成为基于 Bourne shell 代码的第二个符合 POSIX 标准的 shell

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

答案3

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

-bash-4.2$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec  4 16:48 /bin/sh -> bash
-bash-4.2$ 

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

答案4

这 ”#!”注释并不总是使用/bin/bashor /bin/sh。它只是列出了解释器应该是什么,而不仅仅是 shell 脚本。例如,我的 python 脚本通常以#!/usr/bin/env python.

#!/bin/sh现在和之间的区别#!/bin/bash在于,它/bin/sh并不总是到 的符号链接/bin/bash。经常但并非总是如此。 Ubuntu 是一个值得注意的例外。我见过脚本在 CentOS 上运行良好,但在 Ubuntu 上失败,因为作者使用了 bash 特定的语法#!/bin/sh

相关内容