grepping、awking、sedding 和 pipeline 是任何类 Unix 操作系统的用户的日常例程,可能是在命令行上,也可能是在 shell 脚本中(统称为过滤器今后)。
从本质上讲,当使用“标准”Unix CLI 程序和 shell 内置程序(统称为命令从现在开始),过滤器在每个过滤器步骤中都需要精确的 stdin、stdout 和 stderr 预期格式才能正常工作。下面我将某些命令的这种精确预期格式称为该命令的 API。
作为一个有Web开发背景的人,我将这种数据收集和数据处理在技术上与网页抓取- 只要数据表示有最轻微的变化,这种技术就非常不稳定。
我现在的问题与 Unix 命令 API 的稳定性有关。
- 类 Unix 操作系统中的命令在输入和输出方面是否遵循正式的标准化?
- 历史上是否存在对某些重要命令的更新导致破坏使用该命令的旧版本构建的某些过滤器的功能的情况?
- Unix 命令是否随着时间的推移而成熟,绝对不可能以可能破坏某些过滤器的方式进行更改?
- 如果过滤器可能因命令 API 的更改而有时会损坏,作为开发人员,我如何保护我的过滤器免受此问题的影响?
答案1
POSIX 2008 标准有一个部分描述“壳牌和公用事业”。一般来说,如果您坚持这一点,您的脚本应该是相当面向未来的,除了可能的弃用之外,但这些几乎不会在一夜之间发生,因此您应该有足够的时间来更新您的脚本。
在某些情况下,单个实用程序的输出格式在不同平台和版本之间差异很大,POSIX 标准可能包括一个通常称为-p
或的选项-P
,该选项指定有保证且可预测的输出格式。这方面的一个例子是time
公用事业,其实现差异很大。如果您需要稳定的 API/输出格式,您可以使用time -p
.
如果您需要使用 POSIX 标准未涵盖的过滤器实用程序,那么您几乎会受到发行版打包者/上游开发人员的摆布,就像您在进行网页抓取时受到远程 Web 开发人员的摆布一样。
答案2
我会尝试根据我的经验来回答。
命令并不真正遵守正式规范,但它们确实遵守使用和生成面向行文本的要求。
是的当然。在 GNU 实用程序成为事实上的标准之前,许多供应商都会有奇怪的输出,特别是在
ps
和 方面ls
。这造成了很多痛苦。如今,只有 HP 提供超级古怪的命令。从历史上看,Berkeley Software Distribution (BSD) 实用程序是与过去的重大突破。 POSIX 规范与过去决裂,但现在已被广泛接受。随着时间的推移,Unix 命令确实已经成熟。破坏一些为旧版本编写的脚本仍然不是不可能的。考虑一下最近将 UTF-8 作为文本文件编码的趋势。此更改需要更改基本实用程序,例如
tr
.过去,简单文本几乎总是 ASCII(或接近的字符),因此大写字母形成数字范围,小写字母也是如此。对于 UTF-8 来说,情况不再如此,因此tr
可以接受不同的命令行选项来指定“大写”或“字母数字”等内容。“加固”过滤器的最佳方法之一是不依赖于特定的文本布局。例如,不要执行
cut -c10-24
,这取决于行的位置。改为使用cut -f2
,这会删除第二个制表符分隔的字段。awk
将任何输入行分成 $1、$2、$3...,默认情况下以空格分隔。依赖于诸如“字段”之类的高级概念,而不是诸如列位置之类的低级概念。另外,使用正则表达式:sed
并且awk
都可以使用正则表达式执行不关心输入差异的操作。另一个技巧是将输入处理为过滤器可以挑剔的格式。用于tr -cs '[a-zA-z0-9]' '[\n]'
将文本分成每行一个单词,不带标点符号。在这种情况下,您只是不关心输入文本是什么样的。
答案3
首先,非常简短地回答您的问题:
- 输入/输出约定的正式标准化:不
- 过去由于输出变化而造成的损坏:是的
- 绝对不可能打破未来的过滤器:不
- 我如何保护自己免受变化的影响:保守一点
当您说“API”时,您使用的术语(无论好坏)意味着过滤器输入/输出约定过于正式。非常广泛地(我的意思是“非常”),易于过滤的数据的主要约定是
- 每个输入行都是一个完整的记录
- 在每个记录中,字段由已知的分隔符分隔
一个典型的例子是 /etc/passwd 的格式。但是,这些默认约定在某种程度上被违反的情况可能比严格遵守的情况要多。
- 有很多过滤器(通常用 awk 或 perl 编写)可以解析多行输入格式。
- 有许多输入模式(例如,/var/log/messages)没有明确定义的字段结构,并且必须使用更通用的基于正则表达式的技术。
您的第四个问题,如何保护自己免受输出结构变化的影响,实际上是您唯一可以做的问题。
- 作为@jw013 说,看看posix标准是怎么说的。当然,posix 并没有指定您想要用作输入源的所有命令。
- 如果您希望脚本是可移植的,请尽量避免您碰巧安装的某个命令的任何版本的特性。例如,标准 unix 命令的许多 GNU 版本都有非标准扩展。这些可能很有用,但如果您想要最大的可移植性,则应该避免使用它们。
- 尝试了解哪些命令参数子集和输出格式在跨平台上趋于稳定。不幸的是,这需要随着时间的推移访问多个平台,因为这些差异不会被记录在任何地方,即使是非正式的。
最后,您无法完全保护自己免受您担心的问题的影响,并且没有一个地方可以寻找某个命令应该做什么的“明确”声明。对于许多 shell 脚本,尤其是那些为个人或小规模使用而编写的脚本,这根本不是问题
答案4
只有事实上的 IO 标准——空格和 null 分隔的输出。
至于兼容性,我们通常会转而检查各个过滤器的版本号。并不是说它们改变了太多,而是当您想要使用全新功能并且仍然希望脚本在旧版本上运行时,您必须以某种方式“ifdef”它。除了手动编写测试用例之外,实际上没有能力报告机制。