什么时候必须使用#!/ bin / bash和何时#!/ bin / sh?


104

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


55
当您使用bash函数和语法而不是sh函数和语法时。
Mokubai


3
如果它可以帮助任何人,我注意到如果您的脚本带有shebang ,vim它将突出显示bash-isms #!/bin/sh。我仅将其更改为bash如果事情变得毛茸茸以开始需要bash功能。
SeldomNeedy

@SeldomNeedy默认情况下,它突出显示的某些内容在任何POSIX shell中都可以正常工作。$(...)特别令人讨厌。此外,它们中的一些微妙[ <(...)cmd >& file没有得到任何错误高亮显示,例如,他们只是没有特殊高亮他们的意思,带或不带g:is_bash]
Random832

@ Random832看起来vim的问题跟踪器中最近有一些有关此主题的活动。
SeldomNeedy

Answers:


150

简而言之:

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

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

  • 如果你开始使用Bash特殊功能(如数组),你应该特别要求的bash -因为,即使/bin/sh已经调用上的bash 你的系统,它可能无法在其他人的系统,你的脚本将不会运行那里。(当然,zsh和ksh也是如此。)您可以使用shellcheck来识别bashisms。

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

然而:

  • 甚至#!/bin/bash不是很正确。在不同的系统上,bash可能驻留在/usr/bin/usr/pkg/bin或中/usr/local/bin

  • 一个更可靠的选项是#!/usr/bin/env bash,它使用$ PATH。(尽管env也没有严格保证该工具本身,/usr/bin/env但它仍然可以在比其他系统更多的系统/bin/bash上使用。)


10
好答案。您是要使其成为CW吗?
Mokubai

3
请注意,如果bash从此/bin/sh开始运行,它将尝试模拟sh功能(请参见手册页),不确定bash特定的功能在此模式下是否可用。env该工具是posix工具,应该在大多数发行版中都可以找到,但是我不确定,因为有些人可能不尊重posix。
布莱斯

8
不,实际上POSIX 特别声明不能假定它位于/bin:“ 应用程序应注意,不能假定shell的标准PATH是/ bin / sh或/ usr / bin / sh,应确定通过查询getconf PATH返回的PATH,确保返回的路径名是绝对路径名,而不是内置的shell。另请参阅此问题,尤其是此答案
terdon

12
嗯,那么,这意味着POSIX实际上没有定义一种使#!脚本工作的可移植方式吗?
grawity

4
POSIX sh 不是 Bourne:它是早期ksh的派生产品,而不是 Bourne的直接后代。区分它们是容易的:echo foo ^ cat发射foo ^ cat在POSIX SH,并且仅发射foo布赫纳(作为^一个管道字符那里)。
查尔斯·达菲

18

使用与您实际用于开发和调试脚本的外壳相对应的shebang。也就是说,如果您的登录shell是bash,并且您在终端中以可执行文件身份运行脚本,请使用#!/bin/bash。不要仅仅假设既然您还没有使用数组(或者bash您知道的任何功能),那么就可以安全地选择所需的任何shell。外壳之间有许多细微的差异(echo,函数,循环,您可以将其命名),如果不进行适当的测试就无法发现它们。

考虑一下:如果您离开#!/bin/bash而用户没有,则他们将看到清晰的错误消息,例如

Error: /bin/bash not found

大多数用户可以通过安装适当的软件包在一分钟内解决此问题。另一方面,如果您将shebang替换为,#!/bin/sh并且在/bin/sh与的符号链接为的系统上对其进行测试/bin/bash,则没有此功能的用户bash会遇到麻烦。他们很可能会看到类似以下错误消息:

Error in script.sh line 123: error parsing token xyz

这可能要花几个小时才能解决,也没有任何线索说明他们应该使用哪个外壳。

您并不需要在shebang中使用其他shell的理由并不多。原因之一是您使用的外壳不普及。另一个是sh在某些系统上获得更快的性能,而您的脚本将成为性能瓶颈。在这种情况下,请使用目标外壳程序彻底测试脚本,然后更改shebang。


@Hastur我没有收到您的评论。shebang肯定会定义将使用哪个shell来运行脚本,您是否认为我的回答暗含其他含义?
德米特里·格里戈里耶夫

1
算了吧。我误解了“为什么要使用”部分 ...
Hastur

6

您只能使用#! /bin/sh

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

您只应该编写可与任何外壳程序语言实现一起使用的外壳程序脚本(包括与外壳程序本身一起使用的所有“实用程序”程序)。这些天,您可能可以将POSIX.1-2001不是 -2008)作为Shell和实用程序功能的权威,但是请注意,可能有一天您可能会被要求将脚本移植到旧系统(例如Solaris)或AIX),其外壳和实用程序大约在1992年被冻结。

什么,认真吗?

是的,认真。

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

这是另一回事:随机选择的Unix安装上更有可能使用Perl 5核心解释器(/usr/bin/perl)的某些迭代。其他好的脚本语言(Python,Ruby,node.js等-与shell相比,我什至包括PHP和Tcl在内)也大致可以作为bash和其他扩展shell使用。(/(usr|opt)(/(local|sfw|pkg)?)?/bin/bash

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

现在,简单的 shell脚本(这种脚本只是从cron作业或某些东西中按顺序运行几个程序),将它们保留为shell脚本没有任何问题。但是简单的shell脚本甚至不需要数组或函数[[。而且,只有在别无选择时,才应该编写复杂的 Shell脚本。例如,Autoconf脚本仍然是正确的Shell脚本。但是这些脚本必须在与要配置的程序相关的所有形式上运行/bin/sh。这意味着他们不能使用任何扩展名。这些天您可能不必在意旧的专有Unix,但是您可能应该在意当前的开源BSD,其中有些没有安装bash默认情况下,以及仅给您最少的shell和的嵌入式环境busybox

总之,当您发现自己想要的功能在便携式Shell语言中不可用时,这表明该脚本变得太复杂而无法保留Shell脚本。改用更好的语言重写它。


4
抱歉,这有点愚蠢。是的,如果您要编写的内容将i)分发给ii)在不同的环境中使用,则应坚持basic sh。但是,这与声明永远不要使用其他外壳程序相去甚远。每天有成千上万个(可能更多)脚本编写,其中绝大部分只能在一台计算机上运行。您的建议对生产代码有意义,但对大多数脚本所编写的日常“ sysdaminy”作业却没有意义。如果我知道我的脚本只能由我使用并且在Linux机器上使用,bash很好。
terdon

1
@terdon关键是,如果您可以选择编写bash脚本,那么您还可以选择编写perl(或其他)脚本,这总是更好的选择。
zwol16年

@terdon答案肯定不是很傻,您的评论只是天真。与Perl相比,Shell脚本调试起来很糟糕,为此,他们可以轻松地将完整的IDE与图形化调试结合使用。此外,每天为某些小事情编写的大多数脚本都倾向于更改和重新更改,增长,作为示例复制到同事或网上等。很可能没有理由冒这样的风险。
ThorstenSchöning16年

@ThorstenSchöning我有点傻。如果这是Unix&Linux,甚至是Stack Overflow,我什至可以同意。如果我们是在谈论一般的最佳实践或专业程序员,那么最好是坚持使用基本的POSIX sh。但是,在我看来,这是一个面向最终用户的超级用户网站,该网站告诉人们在编写Shell脚本时切勿使用bash或zsh,这是极端的做法。
terdon

@terdon 在我看来,实际上,阻止最终用户编写复杂的shell脚本更为重要。专业人士的平均技术水平较高(因此,他们更有可能应付复杂的shell脚本的陷阱),应该准确地知道他们需要移植到哪些环境,并得到报酬以放弃感到沮丧
zwol

4

通常,如果时间比功能更重要,则可以使用速度更快的Shell。sh通常被别名为破折号,并且倾向于用于root的cron任务或批处理操作,其中每一秒都非常重要。


2
从文件系统中加载额外的shell解释器可以抵消这种优势,而纳秒级(与实际机器周期的数量级相同)是C语言和汇编语言程序员的工作领域。
rackandboneman

纳秒级的cron工作只有在您拥有数十亿个工作时才有所不同,而且cron无论如何都无法处理那么多的工作。
德米特里·格里戈里耶夫

1
即使mckenzm有点夸张,也不能否认拥有更多性能会更好!不要挂在“纳米”部分。(纳秒甚至不计入C,除非它是实时系统之类的内部循环!)
jpaugh

1
@jpaugh除非您使用的(旧版)系统假定某些操作将至少花费特定时间,否则请使用@jpaugh。它发生了!
JAB

3

简而言之,sh如果跨大多数系统的可移植性最重要,并且bash如果bash版本支持的话,如果您想使用其某些特定功能(例如数组),则使用它。


1
  • sh(在大多数情况下),bash(如果有特殊要求)(或ksh或其他)

最安全的道路。当进行硬编码时,将是/ usr / bin /,shellOfChoice但是我现在尝试始终使用的新约定-由于通过更改PATH的“默认位置”可能会更改为:

#!/ usr / bin / env sh或
#!/ usr / bin / env bash
或例如perl脚本
#!/ usr / bin / env perl -w

当然,当您有理由不要让脚本自动采用新的PATH时,请继续对其进行硬编码-然后/ usr / bin / something应该是最可能使用的路径。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.