协慌网

登录 贡献 社区

sh 和 bash 之间的区别

在编写 shell 程序时,我们经常使用/bin/sh/bin/bash 。我通常使用bash ,但我不知道它们之间有什么区别。

bashsh之间的主要区别是什么?

在使用bashsh编程时我们需要注意什么?

答案

什么是 sh

sh (或 Shell 命令语言)是POSIX 标准描述的编程语言。它有很多实现( ksh88dash ,...)。 bash也可以被认为是sh的实现(见下文)。

因为sh是规范而不是实现,所以/bin/sh是大多数 POSIX 系统上实际实现的符号链接(或硬链接)。

什么是 bash

bash最初是一个与sh兼容的实现(虽然它早于 POSIX 标准几年),但随着时间的推移,它已经获得了许多扩展。其中许多扩展可能会改变有效 POSIX shell 脚本的行为,因此bash本身并不是一个有效的 POSIX shell。相反,它是 POSIX shell 语言的方言。

bash支持--posix开关,这使它更符合 POSIX 标准。如果调用sh它也会尝试模仿 POSIX。

sh = bash?

很长一段时间, /bin/sh曾经指向大多数 GNU / Linux 系统上的/bin/bash 。结果,忽略两者之间的差异几乎是安全的。但最近这种情况开始发生变化。

/bin/sh未指向/bin/bash (以及其中某些/bin/bash甚至可能不存在)的系统的一些常见示例是:

  1. 现代 Debian 和 Ubuntu 系统,其符号链接shdash默认;
  2. Busybox ,通常在 Linux 系统启动时作为initramfs一部分运行。它使用ash shell 实现。
  3. BSD,通常是任何非 Linux 系统。 OpenBSD 使用了 Korn shell 的后代pdksh 。 FreeBSD 的sh是原始 UNIX Bourne shell 的后代。 Solaris 有自己的sh ,很长一段时间不符合 POSIX 标准; Heirloom 项目提供免费实施。

你怎么能找到你的系统上/bin/sh指向的内容?

复杂的是/bin/sh可以是符号链接或硬链接。如果它是一个符号链接,解决它的便携方式是:

% file -h /bin/sh
/bin/sh: symbolic link to bash

如果这是一个硬链接,请尝试

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash

事实上, -L标志包括符号链接和硬链接,但这种方法的缺点是它不可移植 - 虽然GNU findFreeBSD 都支持它,但 POSIX 不需要 find来支持-samefile选项。

Shebang 线

最终,由你来决定使用哪一个,通过编写 «shebang» 系列。

例如

#!/bin/sh

将使用sh (以及任何恰好指向的东西),

#!/bin/bash

如果它可用,将使用/bin/bash如果不可用则失败,并显示错误消息)。当然,您也可以指定其他实现,例如

#!/bin/dash

哪一个使用

对于我自己的脚本,我更喜欢sh ,原因如下:

  • 它是标准化的
  • 它更简单,更容易学习
  • 它可以跨 POSIX 系统移植 - 即使它们没有bash ,它们也需要sh

使用bash也有好处。它的特点使编程更方便,类似于其他现代编程语言的编程。这些包括范围局部变量和数组。 Plain sh是一种非常简约的编程语言。

shhttp//man.cx/sh
bashhttp//man.cx/bash

TL; DRbashsh的超集,具有更优雅的语法和更多功能。在几乎所有情况下使用 bash shebang 系列是安全的,因为它在现代平台上非常普遍。

注意:在某些环境中, sh bash 。检查sh --version

这个问题经常被提名为试图使用sh人的规范,并且惊讶于它的行为与bash 。以下是常见误解和陷阱的快速概述。

首先,您应该了解会发生什么。

  • 如果使用sh scriptname运行脚本,或者使用scriptname运行脚本并在shebang行中使用#!/bin/sh ,则应该期望 POSIX sh行为。
  • 如果您使用bash scriptname运行脚本,或者使用scriptname运行它并且在 shebang 行中有#!/bin/bash (或本地等效项),那么您应该期望 Bash 行为。

拥有正确的 shebang 并通过仅键入脚本名称(可能具有相对路径或完整路径)来运行脚本通常是首选解决方案。除了正确的 shebang 之外,这还要求脚本文件具有执行权限( chmod a+x scriptname )。

那么,它们实际上有何区别?

Bash 参考手册有一个部分试图列举差异,但一些常见的混淆来源包括

  • [[不适用于sh (仅限于[更笨重且有限])。
  • sh没有数组。
  • 某些 Bash 关键字(如localsourcefunctionselect不可移植到sh 。 (有些sh实现支持例如local 。)
  • Bash 有许多 C 风格的语法扩展,如$'string\nwith\tC\aescapes'for((i=0;i<=3;i++))循环, +=增量赋值等的三参数。
  • Bash 支持<<<'here strings'
  • Bash 有*.{png,jpg}{0..12}支撑扩展。
  • ~仅在 Bash 中引用$HOME (更常见的是~username名主目录的username )。 这是在 POSIX 中,但可能正在从一些 pre-POSIX /bin/sh实现中产生。
  • Bash 的进程替换为<(cmd)>(cmd)
  • Bash 具有 Csh 样式的方便重定向别名,如&|2>&1 |&> for > ... 2>&1
  • Bash 通过<>重定向支持协处理。
  • Bash 具有一组丰富的扩展非标准参数扩展,例如${substring:1:2}${variable/pattern/replacement} ,case 转换等。
  • Bash 显着扩展了 shell 算法的设施(尽管仍然没有浮点支持)。
  • 许多 Bash-only 扩展,用于启用或禁用可选行为并公开 shell 的内部状态。
  • 用于交互式使用的许多便利功能然而不影响脚本行为。

请记住,这是一个简略的列表。有关完整的独家新闻,请参阅参考手册; 有关很多良好的解决方法,请参阅http://mywiki.wooledge.org/Bashism ; 和 / 或尝试http://shellcheck.net/警告许多 Bash 功能。

一个常见的错误是有一个#!/bin/bash shebang 行,但是然后使用sh scriptname来实际运行脚本。这基本上禁用了任何仅限 Bash 的功能,因此您会遇到语法错误,例如尝试使用数组。

不幸的是,当你尝试使用这些结构作为sh时,Bash 不会发出警告。它不完全禁用所有猛砸唯一的功能,要么,所以通过调用它运行 bash 作为sh不检查,如果你的脚本是正确移植到一个好办法ash / dash / POSIX sh或类似变种传家宝sh