最便携的 shell 是什么,以及要遵循的相关最佳实践?

最便携的 shell 是什么,以及要遵循的相关最佳实践?

我热衷于 shell 脚本已有几个月了,发现我编写的代码(在bash)在某些机器上不起作用。这是非常令人失望的。

我意识到我应该学习最多相反,可移植的 shell 脚本。当然,一开始会有点痛苦,但我相信它最终会得到回报。我希望我的 shell 脚本几乎可以在所有地方运行,包括 Linux 系列和 BSD 系列。 (但我不在乎它们是否在 Windows 上运行。)

问题

  1. 最便携的外壳是什么?编辑:请忽略这个问题

  2. 使用最便携的 shell 有哪些严重的缺点?编辑:请忽略这个问题

  3. 如果我想写出可移植性极高的代码,还需要注意什么?例如,我应该使用和避免哪些 core-utils 以及哪些版本?

答案1

您的bash脚本应该在bash安装了 shell 的任何计算机上运行,​​除非您使用的bash功能对于其他系统上的 shell 版本而言太新,例如尝试使用名称引用 ( declare -n)、关联数组 ( declare -A) 或macOS 上默认%(...)T的格式(或任何其他不同的东西)(这是古老的;在这种情况下只需使用 Homebrew 安装更新的版本)。如果您的脚本使用其他系统上不可用的外部工具,或者可能未实现或以不同方式实现的工具功能,则您的脚本也可能会失败。printfbashbash

POSIX 系统上最可移植的 shell 语言是shshell。此处对此进行了描述:https://pubs.opengroup.org/onlinepubs/9699919799/idx/shell.html

shell bash(和其他)实现了sh,并带有扩展。有些 shell,例如fishzsh,甚至根本不尝试成为shshell(而是通过zsh提供一定程度的“sh仿真” --emulate sh),而像这样的 shelldash 尝试更加纯粹地遵循 POSIXsh标准(但仍然有一些扩展)。

一般来说,与相关的 shell 方面句法内置功能只要脚本始终由相同的 shell(以及 shell 的版本,在某种程度上)执行,即使该 shell 恰好是 shell,也应该是“可移植的” fish。这与 Python、Perl 和 Ruby 脚本具有相同类型的“可移植性”。然而,应该始终有一个sh可用的 shell(通常安装为/bin/sh),它通常是bashdashksh以某种形式的“兼容模式”运行。

POSIX 系统上最可移植的实用程序是 POSIX 实用程序。这些描述如下:https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html

GNU 实用程序,即 中的实用程序coreutils,实现了 POSIX 实用程序(作为用户 schily在评论中指出,并不意味着它们是 POSIX-合规的),然后用主要为了方便起见的功能对其进行扩展。对于其他 Unix 系统上的相应实用程序也是如此。另请注意,Linux 上的 GNUcoreutils软件包并不包含 POSIX 指定的所有实用程序,并且诸如findsedawk等实用程序都是 POSIX 实用程序,它们是单独打包的。

只要您继续使用 POSIX 实用程序并使用其 POSIX 行为(在选项等方面),在使用 POSIXsh语法的脚本中,您就可能在大多数 Unix 系统上“大部分可移植”。但还要注意,有非 POSIX 实用程序和 POSIX 实用程序的非 POSIX 扩展仍然存在相当可移植的,因为它们被普遍实现。常见的非 POSIX 实用程序的示例有pkilltargzip。常见的 POSIX 实用程序的非 POSIX 扩展示例是通常可以理解-iname的谓词find和事实sed扩展正则表达式使用-E.

您将通过自己测试(例如在虚拟机中)以及阅读本网站上的问题和答案,了解哪些工具的实现中的哪些扩展、在哪些 Unices 上运行、以何种方式运行。例如,如何使用 sed -i (就地编辑)实现可移植性?

另请注意,在某些情况下,POSIX 标准会保留“未指定”行为,这意味着两个不同 Unix 系统上使用 POSIX 选项的相同实用程序可能会表现不同。一个例子可以在这里找到为什么在权限正常的情况下符号链接更新到新目标时权限被拒绝?

坏处尝试编写纯 POSIX shell 代码的最大缺点是您会错过一些真正有用的功能。我曾经尝试为findGNU 的谓词测试编写 POSIX 等效测试-readable,这并不容易,并且尝试使用psand 而grep不是通过名称来表示进程pkill是该站点上一些太多问题的主题。同样,尝试“保持 POSIX”,您需要远离真正有用的东西就像perl,您将无法以可移植的方式执行大部分管理任务(添加用户、管理备份、在系统之间传输文件等)

一般来说我更喜欢一个务实完成工作的方法,同时了解我的代码可能需要修改以支持其他系统(如果它曾经在其他系统上使用过),而不是纯粹主义者方法。这并不能阻止我编写我知道将有尽可能少的可移植性问题的脚本(我不会在任何地方使用不可移植的语义)。但这是我个人的看法。

答案2

要点是POSIX就是把这些东西标准化,所以

  1. bash 很好,但使用 POSIX 子集。
  2. 你会失去一些效率
  3. 阅读 POSIX 规范的其余部分,在这里闲逛,以及其他地方,例如新闻组

答案3

如果您想在 GNU/Linux 系统上编写可移植脚本,最好的选择是使用 shell dash。这是一个非常便携的 shell,几乎不提供超出标准的额外功能。这是/bin/shDebian 和 Ubuntu 系统的标准。 Red Hat 和 CentOS 使用 bash 作为它们的/bin/sh所以它们不适合测试可移植性。

答案4

你的问题似乎是错误的。

正确的问题是:“要怎么做才能得到可移植的脚本?”

一个主要的可移植性问题是实现增强功能的程序和使用这些增强功能的用户,因为这些增强功能很可能在不同的 UNIX 平台上缺失。

以下是导致可移植性问题的常见原因的列表:

  • GNU 程序宣传诸如 GNU 风格的长选项--long是 100% 不可移植的。切勿在可移植脚本中使用它们。

  • bash 实现ksh式增强功能,例如[[ -z $HOME ]].这是 POSIX 中没有的功能,只有 ksh88、ksh93、bash、mksh 实现它。

  • bash(除非使用 Mac OS 和 Solaris 使用的编译选项进行编译)实现了echo与 UNIX 行为冲突的非 POSIX 内置命令。要小心,但还要注意:

  • printf 也不是最终的解决方案,因为有些printf实现不能正确处理字符串参数中的 nul 字节,并且不同的 printf 实现具有不同的功能集。

  • 除 1、2、3、6、9、14 和 15 之外的信号编号是不可携带的。相关信号在不同的 UNIX 版本上具有不同的名称/编号映射。

  • POSIX shell 数学之类echo $(( a + b ))可能会导致可移植性问题,因为不同的 shell 具有不同的受支持功能集。请注意,POSIX 仅需要一组有限的功能。

  • POSIX shell 通常是不是因为/bin/shPOSIX 不处理路径名,除非路径名需要dev/tty存在。如果您想启动 POSIX shell,请调用getconf PATH并使用结果作为PATH您的 shell,然后键入sh,您将获得一个 POSIX shell。

  • 即使人们期望的/bin/usr/bin通常的所有其他实用程序也不是实用程序的 POSIX 变体。您需要致电:

    PATH=`getconf PATH`
    export PATH
    sh
    

    获取 POSIX shell,此 POSIX shell 调用实用程序的 POSIX 变体。

许多人建议用于dash测试,但这是一个问题,因为dash它不支持 POSIX,因为它不实现对多字节字符的支持。

我建议使用pbosh它是bosh.pbosh实现默认情况下打开的最小 POSIX 功能。

相关内容