是否有一个最低符合 POSIX.2 标准的外壳



在以下意义上,是否有一个最低符合 POSIX.2 的 shell(我们称之为 mpcsh ):

如果mpcsh myscript.sh在我的(合规)系统上行为正常,那么对于任何兼容系统上的任何 POSIX.2 兼容外壳xshxsh myscript.sh的行为将相同。 ("相同"到不太相关的东西,如错误消息的措辞等)

达世币符合条件吗?

如果没有,有没有办法验证myscript.sh的合规性?

<小时 />

编辑(9年后):

接受的答案仍然有效,但请查看此博客文章和checkbashisms命令(来源)。避免 bashisms 与编写符合 POSIX.2 的 shell 脚本不同,但它很接近。

提前给出的悲哀答案

它不会帮助你(不像你期望和想要的那么多和可靠)。


原因如下。

虚拟"POSIX shell"无法解决的一个大问题是标准中措辞模棱两可或没有解决的事情,因此shell可以在遵守标准的同时以不同的方式实现事情。

以这两个关于管道的例子为例,第一个是众所周知的:

示例 1 - 范围界定

$ ksh -c 'printf "foo" | read s; echo "[${s}]"'
[foo]
$ bash -c 'printf "foo" | read s; echo "[${s}]"'
[]

ksh执行当前外壳中管道的最后一个命令,而bash执行子外壳中的所有命令,包括最后一个命令。 bash 4引入了lastpipe选项,使其行为类似于ksh

$ bash -c 'shopt -s lastpipe; printf "foo" | read s; echo "[${s}]"'
[foo]

所有这些都(有争议地)根据标准:

此外,多命令管道的每个命令都在子 shell 环境中;但是,作为扩展,管道中的任何或所有命令都可以在当前环境中执行。

我不能 100% 确定它们对扩展的含义,但根据文档中的其他示例,这并不意味着 shell 必须提供一种在行为之间切换的方法,而只是它可以,如果它愿意,以这种"扩展方式"实现事物。其他人对此有不同的解读,并争论ksh行为不符合标准,我明白为什么。措辞不仅不走运,而且首先允许这样做不是一个好主意。

在实践中,哪种行为是正确的并不重要,因为这些行为是"两个大外壳",人们会认为,如果你不使用他们的扩展,而只使用据称符合 POSIX 的代码,它就可以在任何一个中工作,但事实是,如果你依赖上面提到的一种或另一种行为,你的脚本可能会以可怕的方式中断。

示例 2 - 重定向

这个我几天前才知道的,请看我的答案:

foo | bar 2>./qux | quux

常识和 POLA 告诉我,当点击下一行代码时,quuxbar 都应该完成运行,这意味着文件./qux已完全填充。右?哈哈

POSIX指出,

如果管道不在后台(请参阅异步列表),shell 应等待管道中指定的最后一个命令完成,也可能等待所有命令完成。

可以(!)等待所有命令完成!咦!

bash等待:

shell 在返回值之前等待管道中的所有命令终止。

但 KSH 不会:

每个命令(可能最后一个除外)都作为单独的进程运行;shell 等待最后一个命令终止。

因此,如果您在管道之间使用重定向,请确保您知道自己在做什么,因为这的处理方式不同,并且可能会在边缘情况下严重中断,具体取决于您的代码。

我可以举另一个与管道无关的例子,但我希望这两个就足够了。

结论

有一个标准是好的,不断修改它更好,坚持它很棒。但是,如果标准由于歧义或宽容而失败,事情仍然会意外地破坏,实际上使标准的有用性无效。

这在实践中意味着,除了编写"POSIX兼容"代码之外,您仍然需要思考并知道自己在做什么来防止某些事情发生。

话虽如此,一个尚未提及的 shell 是 posh 它应该是 POSIX 加上比 dash 更少的扩展(主要是 echo -nlocal 关键字),根据它的手册页:

BUGS
   Any bugs in posh should be reported via the Debian BTS.
   Legitimate bugs are inconsistencies between manpage and behavior,
   and inconsistencies between behavior and Debian policy
   (currently SUSv3 compliance with the following exceptions:
   echo -n, binary -a and -o to test, local scoping).

扬子晚报.

可能最接近规范 shell 的是 ash,它由 NetBSD 基金会和其他组织维护。

这种外壳的下游变体称为dash更为人所知。

目前,POSIX shell没有单一的榜样

自最初的Bourne外壳以来,POSIX外壳采用了许多附加功能。

我知道的所有实现这些功能的 shell 也都有超出 POSIX shell 功能集的扩展。

例如,POSIX 允许以下格式的算术表达式:

var=$(( expression ))

但它不允许等效的:

(( var = expression ))

bashksh93支持。

我知道bash有一个set -o posix选项,但这不会禁用任何扩展。

$ set -o posix
$ (( a = 1 + 1 ))
$ echo $a
2

据我所知,ksh93试图符合开箱即用的 POSIX,但仍然允许扩展。

POSIX开发人员花了数年时间(毫不夸张)努力解决这样一个问题:"应用程序符合标准意味着什么?虽然POSIX开发人员能够定义一个一致性测试套件来实施标准(POSIX.1和POSIX.2),并且可以将"严格符合要求的应用程序"的概念定义为除了标准的强制性元素之外不使用任何接口的应用程序,但他们无法定义一个测试制度来确认特定应用程序"严格符合"POSIX.1。 或者 shell 脚本"严格符合"POSIX.2。

最初的问题就是寻求这一点;验证脚本的一致性测试仅使用完全指定的标准元素。唉,这个标准充满了"黄鼠狼词",它们放松了行为的定义,使得这样的测试实际上不可能用于任何重要级别的有用性脚本。(即使撇开 shell 脚本可以生成和执行 shell 脚本的事实,这也是正确的,从而使"严格符合"的问题等同于停止问题。

(完全披露:从1988年到1999年,我是POSIX系列标准的创建者IEEE-CS TCOS的工作成员和委员会负责人。

如果没有,有没有办法验证 myscript.sh 的合规性?

这基本上是质量保证的一个案例。入手:

  • 代码审查
  • 单元测试(是的,我已经这样做了)
  • 功能测试
  • 使用尽可能多的不同 shell 程序执行测试套件。( ashbashdashksh93mkshzsh

就个人而言,我的目标是bashksh93支持的通用扩展集。他们是可用 shell 语言的最古老、最广泛使用的口译员。

编辑最近我碰巧遇到了rylnd/shpec - 一个用于你的shell代码的测试框架。您可以在测试用例中描述代码的功能,并指定如何验证它们。披露:我帮助它跨bash,ksh和dash工作。

最新更新