AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 865874
Accepted
cherouvim
cherouvim
Asked: 2017-07-30 21:08:01 +0800 CST2017-07-30 21:08:01 +0800 CST 2017-07-30 21:08:01 +0800 CST

#!/bin/sh vs #!/bin/bash 以获得最大的可移植性

  • 772

我通常使用 Ubuntu LTS 服务器,从我理解的符号链接/bin/sh到/bin/dash. 许多其他发行版虽然符号链接/bin/sh到/bin/bash.

据我了解,如果脚本#!/bin/sh在顶部使用它可能不会在所有服务器上以相同的方式运行?

当人们希望这些脚本在服务器之间具有最大的可移植性时,是否有关于使用哪个 shell 来执行脚本的建议做法?

linux
  • 3 3 个回答
  • 6388 Views

3 个回答

  • Voted
  1. Best Answer
    Gordon Davisson
    2017-07-30T22:37:14+08:002017-07-30T22:37:14+08:00

    shell 脚本的可移植性大致有四个级别(就 shebang 行而言):

    1. 最便携:使用#!/bin/shshebang 并仅使用POSIX 标准中指定的基本 shell 语法。这应该适用于几乎任何 POSIX/unix/linux 系统。(好吧,除了 Solaris 10 和更早版本,它具有真正的遗留 Bourne shell,早于 POSIX,因此不兼容,如/bin/sh.)

    2. 第二最便携:使用#!/bin/bash(或#!/usr/bin/env bash)shebang 行,并坚持使用 bash v3 功能。这适用于任何具有 bash 的系统(在预期位置)。

    3. 第三最便携:使用#!/bin/bash(或#!/usr/bin/env bash)shebang 行,并使用 bash v4 功能。这将在任何具有 bash v3 的系统上失败(例如 macOS,出于许可原因必须使用它)。

    4. 最不便携:使用#!/bin/shshebang 并使用 POSIX shell 语法的 bash 扩展。这将在任何具有 bash 以外的 /bin/sh 的系统(例如最近的 Ubuntu 版本)上失败。永远不要这样做;这不仅仅是一个兼容性问题,它完全是错误的。不幸的是,这是很多人犯的错误。

    我的建议:使用前三个中最保守的一个,它提供脚本所需的所有 shell 功能。为了获得最大的可移植性,请使用选项#1,但根据我的经验,一些 bash 功能(如数组)非常有用,我将使用 #2。

    您可以做的最糟糕的事情是#4,使用错误的shebang。如果您不确定哪些功能是基本 POSIX 以及哪些是 bash 扩展,请坚持使用 bash shebang(即选项 #2),或者使用非常基本的 shell(如 Ubuntu LTS 服务器上的 dash)彻底测试脚本。Ubuntu wiki 有一个很好的 bashisms 列表来提防。

    在 Unix 和 Linux 问题“sh 兼容意味着什么?”中有一些关于 shell 的历史和差异的非常好的信息。和 Stackoverflow 问题“sh 和 bash 之间的区别”。

    另外,请注意,shell 并不是不同系统之间唯一不同的东西。如果你习惯了 linux,那么你就习惯了 GNU 命令,这些命令有很多在其他 unix 系统(例如 bsd、macOS)上可能找不到的非标准扩展。不幸的是,这里没有简单的规则,你只需要知道你正在使用的命令的变化范围。

    就可移植性而言,最讨厌的命令之一是最基本的命令之一:echo. 任何时候您将它与任何选项(例如echo -nor echo -e)或字符串中的任何转义符(反斜杠)一起使用时,不同的版本会做不同的事情。任何时候你想打印一个没有换行符的字符串,或者在字符串中使用转义符,请printf改用(并了解它是如何工作的——它比echo现在更复杂)。ps命令也是一团糟。

    另一个需要注意的一般事情是命令选项语法的最新/GNUish 扩展:旧的(标准)命令格式是命令后跟选项(带有单个破折号,每个选项是单个字母),然后是命令参数。最近的(通常是不可移植的)变体包括长选项(通常用 引入--),允许选项出现在参数之后,并--用于将选项与参数分开。

    • 68
  2. Kaz
    2017-07-31T20:47:21+08:002017-07-31T20:47:21+08:00

    在./configure为构建准备 TXR 语言的脚本中,为了更好的可移植性,我编写了以下序言。即使#!/bin/sh是不符合 POSIX 的旧 Bourne Shell,脚本也会自行引导。(我在 Solaris 10 VM 上构建每个版本)。

    #!/bin/sh
    
    # use your own variable name instead of txr_shell;
    # adjust to taste: search for your favorite shells
    
    if test x$txr_shell = x ; then
      for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
        if test -x $shell ; then
           txr_shell=$shell
           break
        fi
      done
      if test x$txr_shell = x ; then
        echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
        txr_shell=/bin/sh
      fi
      export txr_shell
      exec $txr_shell $0 ${@+"$@"}
    fi
    
    # rest of the script here, executing in upgraded shell
    

    这里的想法是我们找到一个比我们正在运行的 shell 更好的 shell,并使用该 shell 重新执行脚本。txr_shell设置环境变量,以便重新执行的脚本知道它是重新执行的递归实例。

    (在我的脚本中,该txr_shell变量随后也用于两个目的:首先,它作为脚本输出中信息性消息的一部分打印。其次,它作为SHELL变量安装在 中Makefile,因此make将使用它shell 也用于执行配方。)

    在 dash 所在的系统上/bin/sh,您可以看到上述逻辑将找到/bin/bash并重新执行该脚本。

    在 Solaris 10 机器上,/usr/xpg4/bin/sh如果没有找到 Bash,就会启动。

    序言是用保守的 shell 方言编写的,test用于文件存在性测试,以及${@+"$@"}扩展参数以适应一些损坏的旧 shell("$@"如果我们在符合 POSIX 的 shell 中)的技巧。

    • 5
  3. zwol
    2017-08-01T07:58:37+08:002017-08-01T07:58:37+08:00

    与 Perl、Python、Ruby、node.js 甚至(可以说)Tcl 等现代脚本语言相比,Bourne shell 语言的所有变体在客观上都非常糟糕。如果您必须做任何甚至有点复杂的事情,从长远来看,如果您使用上述之一而不是 shell 脚本,您会更快乐。

    与那些较新的语言相比,shell 语言仍然具有的一个也是唯一的优势是,可以保证在任何声称是 Unix 的东西上都存在自称的东西。/bin/sh但是,有些东西甚至可能不符合 POSIX 标准;许多遗留的专有 Unix在 Unix95 要求的更改之前/bin/sh冻结了默认 PATH 中实现的语言和实用程序(是的,Unix95,二十年前并且还在继续)。如果幸运的话,可能有一组 Unix95 甚至 POSIX.1-2001 的工具位于不在默认 PATH 上的目录中(例如),但不能保证它们存在。/usr/xpg4/bin

    然而,与 Bash 相比,Perl 的基础知识更有可能出现在任意选择的 Unix 安装中。(我所说的“Perl 的基础知识”是指Perl 5 的某个/usr/bin/perl版本,并且可能是相当老的版本,如果幸运的话,该版本的解释器附带的一组模块也可用。)

    所以:

    如果您正在编写必须在声称是 Unix 的任何地方都可以工作的东西(例如“配置”脚本),您需要使用#! /bin/sh,并且您不需要使用任何扩展。现在我会在这种情况下编写符合 POSIX.1-2001 的 shell,但如果有人要求支持生锈的铁,我会准备修补 POSIXisms。

    但是,如果您不是在编写必须在任何地方都可以使用的东西,那么当您完全想使用任何 Bashism 时,您应该停下来用更好的脚本语言重写整个东西。你未来的自己会感谢你。

    (那么什么时候适合使用 Bash 扩展?第一顺序:从不。第二顺序:仅用于扩展 Bash 交互环境——例如提供智能制表符补全和花哨的提示。)

    • 3

相关问题

  • Linux 主机到主机迁移

  • 如何在 Linux 机器上找到有关硬件的详细信息?

  • 如何在 Linux 下监控每个进程的网络 I/O 使用情况?

  • 在 RHEL4 上修改 CUPS 中的现有打印机设置

  • 为本地网络中的名称解析添加自定义 dns 条目

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve