使Unix shell脚本与POSIX兼容

ktca8awb  于 2022-12-23  发布在  Unix
关注(0)|答案(3)|浏览(174)

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

kuhbmx9i

kuhbmx9i1#

POSIX

第一步是将shebang设置为/bin/shuse shellcheck site以分析脚本,这将指示哪些操作有效或无效以及为什么
例如,将以下脚本粘贴到shellcheck编辑器窗口中:

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

以获得指示:在POSIX sh中,here-string是未定义的。
第二步,可以使用尽可能与POSIX兼容的shell。
is dash是一个与大多数其他简单shell兼容的shell,它是Debian的默认系统shell,是旧的BSD ash的衍生物。
另一个与posix兼容的shell是posh。
但是,破折号和/或豪华可能不适用于某些系统。
还有lksh(带有ksh的味道),目标是与遗留(旧的)shell脚本兼容。
lksh是一个命令解释器,专门用于运行遗留shell脚本。
但是在调用lksh时需要使用选项,比如-o posix-o sh
请注意,强烈建议至少使用-o posix选项调用lksh(如果不是同时使用-o posix选项和-o sh),以充分享受与POSIX标准(这可能是您首先使用lksh而不是mksh的原因)或遗留脚本的更好兼容性。
您可以调用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中测试脚本。
就像上面的列表:破折号,豪华,lksh,和bash -posix。
Solaris是一个独立的世界,您可能需要针对/bin/sh和xpg 4/sh进行测试。
随访:
How can I test for POSIX compliance for shell scripts?

sbtkgmzw

sbtkgmzw2#

使用--posix命令行选项启动Bash或在Bash运行时执行'set -o posix'将使Bash更接近POSIX标准,方法是在Bash默认值不同的区域更改行为以匹配POSIX指定的行为。
Reference

bqf10yzr

bqf10yzr3#

注:

  • 这个答案 * 补充了 * user8017719's great answer
  • 根据问题的要求,下文讨论了一种 * 工具 *:虽然它 * 不 * 直接检查POSIX兼容性,但它在 * 多个shell * 中运行给定脚本,特别是包括/bin/sh
  • /bin/sh,系统默认shell,不应该被认为支持除POSIX规定的特性之外的任何特性,尽管在实践中它在不同程度上支持,这取决于具体的实现。因此,在一个平台上成功地通过/bin/sh运行并不 * 保证脚本在另一个平台上也能工作。在广泛使用的shell中,dash最接近于仅包含POSIX特性的shell。
  • 在 * 多个 * shell中成功运行非常重要:
  • 如果您正在编写一个需要在各种shell中"寻源"的脚本。
  • 如果您知道您脚本只会遇到有限的预先知道的shell。

对于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,但是**您可以通过使用SHELLS环境变量将已安装的任何一组shell作为目标。
以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一起使用时没有"失败",您还可以看到,当bash作为sh运行时,-n没有被识别为"选项"。
另一个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 ...命令的别名)。

相关问题