我一直在研究一个shell脚本来自动化一些任务。确保 shell 脚本在大多数平台上正常运行的最佳方法是什么。例如,我一直在使用echo -n
命令将一些消息打印到屏幕上,而无需尾随新行,并且-n
开关在某些 ksh shell 中不起作用。有人告诉我脚本必须符合 POSIX 标准。如何确保脚本符合 POSIX 标准。有工具吗?或者是否有一个外壳只支持最低 POSIX 要求?
POSIX
第一步,它给你指示什么有效或无效以及为什么,是将shebang设置为/bin/sh
并使用shellcheck站点来分析你的脚本.
例如,将此脚本粘贴到shellcheck编辑器窗口中:
#!/bin/sh
read -r a b <<<"$1"
echo $((a+b))
得到一个指示:"在 POSIX sh 中,这里字符串是未定义的"。
作为第二步,你可以使用一个尽可能与 POSIX 兼容的 shell.
一个与大多数其他简单 shell 兼容的 shell 是 dash,Debian 默认系统 shell,它是旧 BSD ash 的衍生物。
另一个与posix兼容的外壳是posh。
但是,破折号和/或豪华可能不适用于某些系统。
有lksh(带有ksh风格),目标是与遗留的(旧的)shell脚本兼容。从其手册:
lksh 是一个命令解释器,专门用于运行遗留的 shell 脚本。
但是在调用 lksh 时需要使用选项,例如-o posix
和-o sh
:
,如果不是同时使用 -o posix 选项和 -o sh,以完全享受与 POSIX 标准(这可能就是您首先使用 lksh 而不是 mksh 的原因)或遗留脚本。
您将调用lksh -o posix -o sh
而不是简单的lksh
。
使用选项是使其他外壳与 POSIX 兼容的一种方法。像 lksh 一样,使用选项-o posix
,像bash -o posix
.
在 bash 中,甚至可以在脚本中打开 POSIX 选项,使用:
shopt -o posix # also with: set -o posix
也可以建立指向bash
或zsh
的本地链接,使两者都像旧的sh
外壳一样。喜欢这个:
$ 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 进行测试。
随访:
如何测试 shell 脚本的 POSIX 合规性?
使用 --posix命令行选项启动 Bash 或在 Bash 运行时执行 'set -o posix' 将导致 Bash 更符合 POSIX 标准,方法是在 Bash 默认值不同的区域中更改行为以匹配 POSIX 指定的行为。
参考
注意:
-
这个答案补充了user8017719的伟大答案。
-
根据问题中的要求,下面讨论了一个工具:虽然它不直接检查 POSIX 合规性,但它在多个 shell中运行给定的脚本,特别是包括
/bin/sh
.-
/bin/sh
,系统默认的shell,不应该被假定支持POSIX规定的功能以外的任何功能,尽管在实践中它确实支持,在不同程度上,取决于具体的实现。因此,在一个平台上通过/bin/sh
成功运行并不能保证脚本可以在另一个平台上工作。在广泛使用的 shell 中,dash
最接近于仅具有 POSIX 功能的 shell。 -
在多个shell 中成功运行非常重要:
- 如果您正在创作需要从各种 shell 中获取的脚本。
- 如果您知道您的脚本只会遇到一组有限的已知预先 shell。
-
对于布丁在吃的证明方法,请考虑使用shall
(我编写的实用程序),它允许您一次调用具有多个shell 的给定脚本或命令,并反馈脚本/命令成功执行了哪些目标 shell。
如果您安装了 Node.js,则可以使用npm install -g shall
轻松安装它(如果没有,请按照上面的链接访问 GitHub 存储库以获取手动安装说明),然后按如下方式使用它:
shall scriptFile
或者,使用临时命令:
shall -c '<shell-commands>'
默认情况下,它调用sh
,如果已安装,则调用dash
、bash
、zsh
和ksh
,但您可以使用SHELLS
环境变量定位已安装的任何一组 shell。
使用 macOS 上的echo -n
命令示例仅针对 shellsh
和bash
:
$ 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示例显示,即使以sh
方式运行,bash
也允许某些特定于Bash的扩展,例如使用非标准[[ ... ]]
条件(假设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.
如您所见,Bash 以sh
身份运行仍然被接受[[ ... ]]
,而dash
是一个(大部分)仅限 POSIX 功能的 shell,失败了,因为 POSIX 只要求[ ... ]
条件(作为test ...
命令的别名)。