通常,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/sh
shell 的实际 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/sh
she-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 兼容模式zsh
yash
pdksh
sh
较少的否则符合规定)。
bash
(Korn shell 的 GNU 答案)实际上是唯一一个被认证为开源 shell(可以说目前仅在维护中,因为其他 shell 通常基于 ksh88,而 ksh88 自 90 年代以来就没有获得任何新功能)符合 POSIX 标准sh
(作为 macOS 认证的一部分)。
当您使用 she-bang 编写脚本时#! /bin/sh -
,您应该使用标准sh
语法(如果您想要可移植,还应该为该脚本中使用的实用程序使用标准语法,解释 shell 时不仅涉及 shell脚本),那么sh
使用该标准语法解释器的哪种实现并不重要( ksh
,bash
...)。
只要您不使用这些 shell,它们是否具有超出标准的扩展并不重要。就像编写 C 代码一样,只要您编写标准 C 代码并且不使用一种编译器(例如gcc
)或另一种编译器的扩展,无论编译器实现如何(只要编译器兼容),您的代码都应该可以正常编译。
在这里,对于你的#! /bin/sh
she-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 的系统上,您可以在bash
和dash
for/bin/sh
之间进行选择dpkg-reconfigure dash
) 4。sh
Debian 附带的脚本在两个 shell 中的工作方式应该相同,因为它们是根据 Debian 策略标准(POSIX 标准的超集)编写的。您可能会发现它们在zsh
的sh
仿真中也可以正常工作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/file
以some-optional-arg
、脚本的路径和原始参数作为参数执行。您可以创建第一行#! /bin/echo test
来查看发生了什么:
$ ./myscript foo
test ./myscript foo
当您使用/bin/sh -
代替时/bin/echo test
,内核会执行/bin/sh - ./myscript foo
,sh
解释存储在中的内容代码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/sh
是zsh
,但后来改为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 系统,链接到:bash
sh
sh
bash
-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脚本应该更可移植。bash
bash
bash
/usr/local/bin/bash
/bin/bash
#!/bin/sh
答案4
这 ”#!”注释并不总是使用/bin/bash
or /bin/sh
。它只是列出了解释器应该是什么,而不仅仅是 shell 脚本。例如,我的 python 脚本通常以#!/usr/bin/env python
.
#!/bin/sh
现在和之间的区别#!/bin/bash
在于,它/bin/sh
并不总是到 的符号链接/bin/bash
。经常但并非总是如此。 Ubuntu 是一个值得注意的例外。我见过脚本在 CentOS 上运行良好,但在 Ubuntu 上失败,因为作者使用了 bash 特定的语法#!/bin/sh
。