【问题标题】:Making Unix shell scripts POSIX compliant使 Unix shell 脚本与 POSIX 兼容
【发布时间】:2017-04-16 09:52:50
【问题描述】:

我一直在编写一个 shell 脚本来自动化一些任务。确保 shell 脚本在大多数平台上运行没有任何问题的最佳方法是什么。例如,我一直在使用 echo -n 命令将一些消息打印到屏幕上而没有尾随的新行,并且 -n 开关在某些 ksh shell 中不起作用。有人告诉我脚本必须符合 POSIX。如何确保脚本符合 POSIX。有工具吗?还是有只支持最低 POSIX 要求的 shell?

【问题讨论】:

  • This UnixSE question 有一些很好的反馈。
  • 请记住,shell 脚本可移植性(POSIX 合规性)问题的一部分是使用的 shell 表示法。问题的另一半是使用 POSIX 命令的可移植(POSIX 兼容)选项,而不是意外使用 POSIX 命令的 GNU 扩展。有时很难避免此类问题。您还需要决定是否可以使用 Perl 或 Python 等非 POSIX 工具。

标签: linux bash shell unix sh


【解决方案1】:

使用 --posix 命令行选项启动 Bash 或在 Bash 运行时执行 'set -o posix' 将导致 Bash 通过更改行为以匹配 POSIX 在以下区域中指定的行为更符合 POSIX 标准: Bash 默认值不同。

Reference

【讨论】:

  • 这很有帮助,但关键短语是更接近:即使 Bash 在 POSIX 兼容模式下运行,许多 Bashism 仍然可用(例如,[[ ... ]] 条件,@987654323 @ (here-strings), ...),所以它本身不会告诉您脚本是否符合 POSIX。换句话说:在使用带有--posixset -o posix 或将其调用为sh 的 Bash 时,消除了与 POSIX 规范的一些完全不兼容的情况。它仍然允许许多特定于 Bash 的扩展,这些扩展将与大多数 POSIX 功能中断仅限 shell,例如 dash
【解决方案2】:

POSIX

第一步,它可以告诉您什么有效或无效以及为什么,将 shebang 设置为 /bin/shuse shellcheck site 以分析您的脚本。
例如,将此脚本粘贴到 shellcheck 编辑器窗口中:

#!/bin/sh
read -r a b <<<"$1"
echo $((a+b))

得到一个指示:“在 POSIX sh 中,这里的字符串是未定义的”。

作为第二步,您可以使用与 POSIX 尽可能兼容的 shell。
一种与大多数其他简单 shell 兼容的 shell,is dash,Debian 默认系统 shell,它是旧 BSD ash 的衍生版本。

另一个与 posix 兼容的 shell 是 posh。

但是,dash 和/或 posh 可能不适用于某些系统。

有 lksh(具有 ksh 风格),其目标是与遗留(旧)shell 脚本兼容。来自其手册:

lksh 是专门用于运行旧版 shell 脚本的命令解释器。

但是调用lksh的时候需要用到options,比如-o posix-o sh

请注意,强烈建议至少使用 -o posix 选项调用 lksh,如果不是同时使用该选项和 -o sh,以充分享受与 POSIX 标准的更好兼容性(这可能是您在第一名)或旧版脚本。

您应该调用lksh -o posix -o sh 而不是简单的lksh

使用选项是使其他 shell 兼容 POSIX 的一种方法。和 lksh 一样,使用选项-o posix,比如bash -o posix

在 bash 中,甚至可以在脚本中打开 POSIX 选项,使用:

shopt -o posix            # also with: set -o posix

也可以创建到bashzsh 的本地链接,这使得两者都像旧的sh shell。像这样:

$ ln -s /bin/bash ./sh
$ ./sh

有很多替代方案(dash、posh、lksh、bash、zsh 等)可以获得一个可以作为 POSIX shell 工作的 shell。

便携

然而,即便如此,以上所有内容并不能保证“可移植性”。

不幸的是,让 shell 脚本“符合 POSIX”通常比让它在任何真实世界的 shell 上运行更容易。

现实世界中唯一明智的建议是在多个 shell 中测试您的脚本。
如上面的列表:dash、posh、lksh 和 bash --posix。

Solaris 是一个独立的世界,您可能需要针对 /bin/sh 和 xpg4/sh 进行测试。

跟进:

How can I test for POSIX compliance for shell scripts?

【讨论】:

    【解决方案3】:

    补充Isaac's great answer

    对于proof-of-the-pudding-is-in-the-eating 方法,考虑使用shall(我编写的一个实用程序),它允许您使用多个调用给定的脚本或命令> shell 一次,并提供有关脚本/命令成功执行的目标 shell 的反馈。

    如果你安装了 Node.js,你可以很容易地用npm install -g shall 安装它(如果没有,请按照上面的链接到 GitHub repo 获取手动安装说明),然后按如下方式使用它:

    shall scriptFile
    

    或者,使用临时命令:

    shall -c '<shell-commands>'
    

    默认情况下,它会调用sh,如果已安装,还会调用dashbashzshksh,但您可以针对已安装的任何一组shell 使用 SHELLS 环境变量。

    使用 macOS 上的 echo -n 命令示例仅针对 shell shbash

    $ SHELLS=sh,bash shall -c 'echo -n hi'
    ✓ sh (bash variant)                       [0.00s]
      -n hi
    
    ✓ bash                                    [0.00s]
      hi
    
    OK - All 2 shells (sh, bash) report success.
    

    在 macOS 上,bash(有效地)充当sh,而echo -n 在与sh 一起使用时没有失败,您还可以看到-n 不是当bash 运行为sh 时,'不被识别为选项

    另一个 macOS 示例表明 bash 允许某些特定于 Bash 的扩展,即使在以 sh 运行时也是如此,例如使用非标准 [[ ... ]] 条件(假设 dash - 在 Ubuntu 系统上充当 sh - 通过Homebrew安装):

    $ SHELLS=sh,bash,dash shall -c '[[ -n nonempty ]] && echo nonempty'
    ✓ sh (bash variant)                       [0.00s]
      nonempty
    
    ✓ bash                                    [0.00s]
      nonempty
    
    ✗ dash                                    [0.01s]
      dash: 1: [[: not found
    
    FAILED - 1 shell (dash) reports failure, 2 (sh, bash) report success.
    

    如您所见,以 sh 运行的 Bash 仍然接受 [[ ... ]],而 dash(主要是)仅支持 POSIX 功能的 shell失败,因为仅支持 POSIX命令[ ... ] 条件(作为test ... 命令的别名)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多