为什么 GNU `sed` 有时会创建一个没有任何分支命令的循环?

为什么 GNU `sed` 有时会创建一个没有任何分支命令的循环?

考虑以下令人惊讶的实验sed代码:

seq 3 | timeout 5s sed 'H;${g;D}'

...如果没有timeout 5s这个,计算机就会挂起(而不是在 5 秒后停止),但首先它会输出:

1
2

或者添加p以无休止地打印保持缓冲区:

seq 3 | timeout 5s sed 'H;${g;p;D}'

奇怪的是,只有D命令似乎会导致无限循环,(IEd尝试用、 或p、 或)替换它P,但它没有很好的记录。这GNU sed info文档说:

'D'
     If pattern space contains no newline, start a normal new cycle as
     if the 'd' command was issued.  Otherwise, delete text in the
     pattern space up to the first newline, and restart cycle with the
     resultant pattern space, without reading a new line of input.

但这并没有警告用户无限循环的可能性。大概会g;D重置内部行计数器,并sed直接跳回$

这种没有分支语句的循环是否记录在任何地方?如果没有,有人可以解释它是如何工作的吗?

答案1

是的,这是一个奇怪的问题,但显然在预期范围内。

让我们一步步走向核心原因。

测试seq 5 | sed 'H;${g;d}'将打印 1 到 4,每行一个数字,但不打印最后一个数字:5,为什么?

脚步:

  • seq 5生成从 1 到 5 的所有数字,每行一个。
  • sed接收到第一行时1,将其存储在保留空间中(在默认情况下添加换行符之后H)。
  • 因为没有其他内容可执行(下一个命令将仅在最后一行执行$),所以该行也会被打印(除了在保持空间中)。
  • 对于每一行,它都会被附加到添加的换行符之后的保留空间并被打印。
  • 在最后一行,命令g;d被执行。第一个调用了此时的整个保留空间,\n1\n2\n3\n4\n5并立即使用d和 停止将其擦除。

要实际查看保留空间的状态,您可以运行以下 sed 脚本:

$ seq 5 | sed 'H;x;l;x;${g;d}'
\n1$
1
\n1\n2$
2
\n1\n2\n3$
3
\n1\n2\n3\n4$
4
\n1\n2\n3\n4\n5$

假设其D工作原理d巨大的不同之处:仅有的当模式空间上没有新行时。从info sed

'D'
如果模式空间包含换行符,则删除模式空间中直到第一个换行符的文本,并使用生成的模式空间重新启动循环,而不读取新的输入行。

如果模式空间不包含换行符,则启动正常的新循环,就像发出“d”命令一样。

因此,当d将 替换为 a时D运行此脚本:

seq 5 | sed 'H;x;l;x;${g;D}'

你会得到无限的输出。删除D第一行:一个数字和一个换行符,是的,但它返回到开头(不擦除),并且1\n2\n3\n4\n5在模式空间中被附加到保留空间。在第一个循环中, a1\n2\n3\n4\n5被附加到\n1\n2\n3\n4\n5保留空间,使其\n1\n2\n3\n4\n5\n1\n2\n3\n4\n5,它加倍。每个周期的确切值并不重要,重要的是它在每个周期都会变大。

现在,如果我们清除 之前的模式空间D,它可能会起作用:

$ seq 5 | sed 'H;x;l;x;${g;z;D}'
\n1$
1
\n1\n2$
2
\n1\n2\n3$
3
\n1\n2\n3\n4$
4
\n1\n2\n3\n4\n5$

确实如此。

没有什么异常,一切都按预期进行,只是出现了令人惊讶的副作用。

相关内容