Unix shell“ stdin / stdout API”的稳定性如何?


20

grepping,awking,sedding和管道是任何类Unix操作系统的用户的日常例行程序,可能是在命令行中还是在shell脚本中(从现在起统称为过滤器)。

从本质上讲,当使用“标准” Unix CLI程序和Shell内置程序(从现在起统称为命令)时,过滤器需要在每个过滤器步骤中为stdin,stdout和stderr提供精确的预期格式,以使其正常工作。在下文中,我将某些命令的这种精确的预期格式称为该命令的API。

作为具有Web开发背景的人,我将这种数据收集和数据处理技术与Web抓取进行了比较 -这种技术在数据呈现方式发生最小变化时就非常不稳定。

我的问题现在与Unix命令API的稳定性有关。

  1. 类似Unix的操作系统中的命令是否在输入和输出方面遵循正式的标准?
  2. 历史上是否存在某些实例,其中一些重要命令的更新导致使用该命令的较早版本构建的某些过滤器的功能中断?
  3. Unix命令是否已经随着时间而成熟,以至于绝对不可能以某种过滤器可能损坏的方式进行更改?
  4. 如果由于更改命令API导致过滤器不时损坏的情况,作为开发人员,我如何保护我的过滤器免受此问题的影响?

Answers:


17

POSIX 2008标准的一节描述了“外壳和实用程序”。通常,如果您坚持认为您的脚本应该具有相当的前瞻性,但可能不推荐使用,但这些脚本很难在一夜之间发生,因此您应该有足够的时间来更新脚本。

在某些情况下,单个实用程序的输出格式在平台和版本之间会有很大差异,POSIX标准可能包含一个通常称为的选项,-p-P指定了保证和可预测的输出格式。time实用程序就是一个例子,该实用程序的实现方式千差万别。如果您需要稳定的API /输出格式,可以使用time -p

如果您需要使用POSIX标准未涵盖的过滤器实用程序,那么您将完全受发行打包程序/上游开发人员的支配,就像您在进行网络抓取时受到远程Web开发人员的支配一样。


12

我将根据经验尝试回答。

  1. 命令实际上并没有遵循正式的规范,但是确实遵循了使用和生成面向行文本的要求。

  2. 当然是。在GNU实用程序成为事实上的标准之前,很多供应商都会产生古怪的输出,尤其是关于psls。这引起了很多痛苦。如今,只有HP提供了超级古怪的命令。从历史上看,伯克利软件发行(BSD)实用程序是对过去的重大突破。POSIX规范是对过去的突破,但现在已被广泛接受。

  3. Unix命令确实已经随着时间而成熟。打破为旧版本编写的脚本仍然不是不可能的。考虑一下最近将UTF-8用作文本文件编码的趋势。此更改需要更改基本的实用程序,例如tr。过去,简单文本几乎始终是ASCII(或近似值),因此大写字母和小写字母都形成一个数字范围。UTF-8不再是这样,因此tr必须接受不同的命令行选项来指定诸如“大写”或“字母数字”之类的内容。

  4. “强化”过滤器的最佳方法之一是不依赖于特定的文本布局。例如,不执行cut -c10-24,这取决于行的位置。使用cut -f2代替,它将切出第二个制表符分隔的字段。 awk将任何输入行分为$ 1,$ 2,$ 3 ...,默认情况下用空格分隔。依赖于诸如“字段”之类的上层概念,而不是依赖于列位置之类的下层概念。另外,请使用正则表达式:sed并且awk都可以使用不关心输入中某些差异的正则表达式执行操作。另一个技巧是将输入处理为某种格式,您的过滤器可能会很挑剔。用于tr -cs '[a-zA-z0-9]' '[\n]'将文本每行分成一个单词,不带标点。你只是不


9

首先,对您的问题的简短回答:

  1. 输入/输出约定的正式标准化:
  2. 过去由于输出更改而造成的损坏:
  3. 绝对不可能破坏未来的过滤器:
  4. 我如何保护自己不受变化:保持保守

当您说“ API”时,您所使用的术语(无论是好是坏)暗示了有关过滤器输入/输出约定的过多形式。非常广泛地(我的意思是“非常”),易于过滤的主要数据约定是

  • 每个输入行都是完整的记录
  • 在每个记录中,字段由已知的定界符分隔

一个经典的例子是/ etc / passwd的格式。但是,这些默认约定在某种程度上可能比其遵循的惯例更常见。

  • 有很多用于解析多行输入格式的过滤器(通常以awk或perl编写)。
  • 在没有明确定义的字段结构的情况下,有很多输入模式(例如/ var / log / messages),必须使用更常规的基于正则表达式的技术。

您的第四个问题,即如何保护自己免受输出结构变化的影响,实际上是您唯一可以做的任何事情。

  • 正如@ jw013所说,请看posix标准所说的。当然,posix并没有指定您要用作输入源的所有命令。
  • 如果您希望脚本具有可移植性,请尝试避免碰巧错开某个命令的任何版本的特质。例如,许多GNU版本的标准unix命令具有非标准扩展名。这些可能很有用,但如果要最大程度的便携性,则应避免使用它们。
  • 尝试了解哪些命令参数子集和输出格式在各个平台上趋于稳定。不幸的是,这需要随时间访问多个平台,因为这些差异不会在任何地方记录下来,即使是非正式的。

最后,您无法完全保护自己免受担心的问题的困扰,并且没有一个地方可以就某个命令应该做什么进行“确定性”声明。对于许多shell脚本,尤其是为个人或小规模使用而编写的脚本,这根本不是问题


5

仅覆盖您问题的1)。

自然,API总是可以随时根据其创建者的意愿进行更改,从而以任何语言破坏依赖的软件。就是说,Unix工具的I / O “ API” 的好主意是几乎没有(可能0x0a是行尾)。一个好的脚本可以使用Unix工具过滤数据,而不是创建数据。这意味着您的脚本可能会因输入或输出规范发生更改而中断,而不是因为脚本中使用的各个工具的I / O格式(同样,实际上没有一种)发生更改(因为某些内容实际上并不存在)无法真正改变)。

通过基本工具列表,我很少将属性归为producer,这与仅使用filter相对:

  • wc-打印字节,单词,行数- 非常简单的格式,因此绝对不可能更改,而且不太可能在脚本中使用。
  • 差异 -已经发展出不同的输出格式,但我还没有听说过任何问题。通常也不会在没有监督的情况下使用。
  • 日期 -现在,我们在这里确实必须注意产生的内容,尤其是在系统区域设置方面。但是,如果您自己不完全指定输出格式,则输出格式为RFC。
  • cal-不用说了,我知道不同系统的输出格式确实有很大的不同。
  • lsw最后 -如果您想解析ls,我无济于事,这不是本来就应该的。还有,谁是最有互动性的列表发布者?如果在脚本中使用它们,则必须注意所做的事情。
  • 在另一篇文章中指出了时间。但是,是的,和ls一样。更多用于交互式/本地使用。bash内置函数与GNU版本有很大不同,并且GNU版本多年来一直存在未修复的错误。只是不要依赖它。

以下是期望特定输入格式比字节流更具体的工具:

  • BCDC-计算器。事情已经变得更加骇人听闻了(确实,我没有在脚本中使用它们),并且可能是非常稳定的I / O格式。

还有另一个发生破损风险更高的区域,即命令行界面。大多数工具在整个系统和整个时间表上都有不同的功能。例子是

  • 使用regex的所有工具 -regex可以根据系统区域设置(例如LC_COLLATE)更改含义,并且在regex实现中存在许多细微差别。
  • 根本不使用花哨的开关。您可以轻松地使用man 1p find例如读取POSIX查找手册页而不是系统手册页。在我的系统上,我需要安装manpages-posix。

而且即使使用这种开关,通常也不会巧妙地引入错误并污染您的数据。大多数程序只会拒绝使用未知开关。

总而言之,我想说shell实际上有可能成为最可移植的语言之一(当您可移植地编写脚本时,它是可移植的)。与发生细微错误的您喜欢的脚本语言进行比较,或者与您愿意放弃的喜欢的已编译程序进行比较。

此外,在极少数情况下,由于不兼容而可能发生破损,这可能不是由于时间所致,而是由于不同系统之间的多样性(这意味着如果它对您有用,那么它将在20年前实现,并将在20年后实现)。 )。这是工具简单性的必然结果。


1

只有事实上的IO标准-空格和空分隔的输出。

至于兼容性,我们通常会恢复检查单个过滤器的版本号。它们的变化不是很大,但是当您要使用全新功能并且仍希望脚本在旧版本上运行时,则必须以某种方式“ ifdef”。除了手动编写测试用例外,几乎没有功能报告机制。


0

脚本确实会中断,某些情况比其他情况更频繁。古老而著名的软件往往保持相对不变,并且无论如何进行更改通常都具有兼容性标志。

用一个系统编写的脚本可以继续工作,但经常破坏另一个系统。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.