我什么时候必须使用#!/bin/bash,什么时候使用#!/bin/sh?

Hen*_*dré 108 linux shell-script

什么时候#!/bin/bash#!/bin/sh在 shell 脚本中更合适?

use*_*686 152

简而言之:

  • 有几个 shell 实现了POSIX sh 规范的超集。在不同的系统上,/bin/sh可能是 ash、bash、dash、ksh、zsh 等的链接。(尽管它始终与 sh 兼容 - 永远不会是 csh 或fish。)

  • 只要您sh只坚持使用功能,您就可以(甚至可能应该)使用#!/bin/sh并且脚本应该可以正常工作,无论它是哪个 shell。

  • 如果您开始使用 bash 特定的功能(例如数组),您应该特别请求 bash – 因为,即使/bin/sh已经在您的系统上调用了 bash ,它也可能不会在其他人的系统上运行,并且您的脚本不会在那里运行。(当然同样适用于 zsh 和 ksh。)您可以使用shellcheck来识别 bashisms。

  • 即使脚本仅供个人使用,您也可能会注意到某些操作系统/bin/sh在升级过程中发生了变化——例如,在 Debian 上它曾经是 bash,但后来被非常少的破折号取代。使用bashisms但#!/bin/sh突然中断的脚本。

然而:

  • 即使#!/bin/bash不是很正确。在不同的系统上,bash 可能存在于/usr/binor/usr/pkg/bin/usr/local/bin.

  • 更可靠的选项是#!/usr/bin/env bash,它使用 $PATH。(虽然env工具本身也没有得到严格保证,/usr/bin/env但仍然可以在更多系统/bin/bash上运行。)

  • 嗯,这意味着 POSIX 实际上并没有定义一种让 `#!` 脚本工作的可移植方式吗? (12认同)
  • 很好的答案。你的意思是让它成为 CW 吗? (10认同)
  • 不,事实上 POSIX [特别说明](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html) 不能假设它在 `/bin` 中:“*应用程序应该注意shell 的标准 PATH 不能假定为 /bin/sh 或 /usr/bin/sh,而应通过查询 getconf PATH 返回的 PATH 来确定,确保返回的路径名是绝对路径名而不是外壳内置*”。另请参阅[这个问题](http://unix.stackexchange.com/q/29608/22222),特别是[这个答案](http://unix.stackexchange.com/a/77586/22222)。 (8认同)
  • POSIX sh **不是** Bourne:它更像是早期 ksh 的衍生物,而不是 Bourne 的直接后代。区分它们很容易:`echo foo ^ cat` 在 POSIX sh 中发出 `foo ^ cat`,而在 Bourne 中只发出 `foo`(因为 `^` 在那里是一个管道字符)。 (4认同)
  • 如果 bash 是从 `/bin/sh` 运行的,那么它会尝试模仿 `sh` 功能(请参阅 [手册页](http://manpages.ubuntu.com/bash.1)),不确定 bash在此模式下可以使用特定功能。[`env` 工具是一个 posix 工具](http://www.unix.com/man-page/posix/1posix/env/) 它应该可以在大多数发行版中找到,但我不确定,因为有些可能没有尊重posix。 (3认同)
  • @grawity:POSIX 定义的方式根本没有`#!` 行。对于 POSIX,在执行时未被识别为二​​进制可执行文件的文件是通过标准 shell 解释器调用的。 (2认同)
  • @grawity 建议在安装时动态更新 `#!` 行:“此外,在支持可执行脚本(`"#!"` 构造)的系统上,建议使用可执行脚本的应用程序使用 ` getconf PATH` 来确定 shell 路径名并在安装时适当地更新 `"#!"` 脚本(例如,使用 *sed*)。[...]”(来自“sh”页面) (2认同)
  • /usr/bin/env 需要挂载 `/usr`。 (2认同)

Dmi*_*yev 18

使用与您实际用于开发和调试脚本的 shell 相对应的 shebang。即,如果您的登录 shell 是bash,并且您在终端中将脚本作为可执行文件运行,请使用#!/bin/bash. 不要只是假设因为您没有使用过数组(或bash您知道的任何功能),所以您可以安全地选择您喜欢的任何 shell。shell(echo函数、循环、你的名字)之间有许多细微的差异,如果没有适当的测试是无法发现的。

考虑一下:如果您离开#!/bin/bash并且您的用户没有它,他们会看到一条明确的错误消息,例如

Error: /bin/bash not found
Run Code Online (Sandbox Code Playgroud)

大多数用户可以通过安装适当的软件包在一分钟内解决这个问题。另一方面,如果您将 shebang 替换为#!/bin/sh并在/bin/sh具有到 符号链接的系统上测试它/bin/bash,那么没有符号链接的用户bash将会遇到麻烦。他们很可能会看到一条神秘的错误消息,例如:

Error in script.sh line 123: error parsing token xyz
Run Code Online (Sandbox Code Playgroud)

这可能需要数小时才能修复,并且不知道他们应该使用哪个 shell。

您想在 shebang 中使用不同的 shell 的原因并不多。原因之一是您使用的 shell 并不普遍。另一个是sh在某些系统上获得显着更快的性能,并且您的脚本将成为性能瓶颈。在这种情况下,请使用目标 shell 彻底测试您的脚本,然后更改 shebang。


zwo*_*wol 5

您应该只使用#! /bin/sh.

永远不要在 shell 脚本中使用 bash(或 zsh、fish 或 ...)扩展。

您应该只编写适用于任何shell 语言实现的shell 脚本(包括与 shell 本身一起使用的所有“实用程序”程序)。这些日子里,你可以很可能采取POSIX.1-2001-2008)作为权威的什么外壳和实用程序能够,但是要知道,你可能有一天在港口脚本遗留系统调用(如的Solaris或 AIX),其 shell 和实用程序大约在 1992 年被冻结。

什么,认真的?!

是的,认真的。

事情是这样的:Shell 是一种糟糕的编程语言。它唯一要做的就是它是每个Unix 安装都保证具有的/bin/sh唯一的脚本解释器。

这是另一件事:核心 Perl 5 解释器 ( /usr/bin/perl) 的某些迭代可能在随机选择的 Unix 安装上可用(/(usr|opt)(/(local|sfw|pkg)?)?/bin/bash。其他优秀的脚本语言(Python、Ruby、node.js 等——与 shell 相比,我什至将 PHP 和 Tcl 包括在该类别中)也大致与 bash 和其他扩展 shell 一样可用。

因此,如果您可以选择编写 bash 脚本,那么您可以选择使用一种并不糟糕的编程语言。

现在,简单的shell 脚本,那种只是从 cron 作业或其他东西中按顺序运行几个程序的那种,将它们保留为 shell 脚本并没有错。但是简单的 shell 脚本不需要数组或函数,[[甚至不需要。并且只有在别无选择的情况下才应该编写复杂的shell 脚本。例如,Autoconf 脚本实际上仍然是 shell 脚本。但是这些脚本必须在与正在配置的程序相关的每个化身上运行/bin/sh。这意味着他们不能使用任何扩展。这些天你可能不必关心旧的专有 Unix,但你可能应该关心当前的开源 BSD,其中一些没有安装bash默认情况下,嵌入式环境只给你一个最小的 shell 和busybox.

总之,当您发现自己需要可移植 shell 语言中没有的功能时,这表明脚本变得太复杂而不能保留 shell 脚本。改用更好的语言重写它。

  • 对不起,但这有点愚蠢。是的,如果你写的东西 i) 分布式和 ii) 到不同的环境,你应该坚持基本的`sh`。然而,这与声明你应该*永远*不使用其他shell相去甚远。每天有数千个(可能更多)脚本编写,其中绝大多数只会在一台机器上运行。您的建议对生产代码有意义,但不适用于大多数脚本所针对的日常“sysdaminy”作业。如果我知道我的脚本只会被我在 Linux 机器上使用,那么 bash 就可以了。 (4认同)