这个sed程序如何运作?

时间:2016-12-10 21:19:17

标签: algorithm sed

我在Stackoverflow上的其他地方发现了这个sed程序,用于打印除文件的最后5行以外的所有内容,我已经分解并逐行注释:

删除-最后5.sed

# create a label "a"
:a

# - on the last line, delete the pattern space and skip any remaining commands
$d

# - increment the current line number
# - append the current line (which was previously the next line)
#   to the pattern space
N

# - on lines 2-5, branch to label "a"
2,5ba

# - print the first line of the pattern space
P

# - delete the first line of the pattern space
# - skip any remaining commands
# - if any text remains in the pattern space, when the next line is read,
#   append to (rather than overwrite) the pattern space
D

一个例子是:

$ echo a b c d e f g h i j | xargs -n1 | sed -f delete-last-5.sed
a
b
c
d
e

然而,我实际上无法弄清楚它是如何工作的!据我所知,程序执行是这样的:

- _: line_number = 1
- _: pattern_space = "a"
- N: pattern_space += "\nb", line_number = 2
- 2,5ba: branch to label "a"
- N: pattern_space += "\nc", line_number = 3
- 2,5ba: branch to label "a"
- N: pattern_space += "\nd", line_number = 4
- 2,5ba: branch to label "a"
- N: pattern_space += "\ne", line_number = 5
- 2,5ba: branch to label "a"

- N: pattern_space += "\nf", line_number = 6
- P: print "a"
- D: pattern_space -= "a\n"
- _: pattern space is not empty after D, so mark pattern space to be appended to rather than overwritten

- _: line_number = 7
- _: pattern_space += "\ng"
- P: print "b"
- D: pattern_space -= "b\n"
- _: pattern space is not empty after D, so mark pattern space to be appended to rather than overwritten

- _: line_number = 8
- _: pattern_space += "\nh"
- P: print "c"
- D: pattern_space -= "c\n"
- _: pattern space is not empty after D, so mark pattern space to be appended to rather than overwritten

- _: line_number = 9
- _: pattern_space += "\ni"
- P: print "d"
- D: pattern_space -= "d\n"
- _: pattern space is not empty after D, so mark pattern space to be appended to rather than overwritten

- _: line_number = 10
- _: pattern_space += "\nj"
- $d: delete pattern_space

如果你收集所有这些印刷陈述,你最终得到:

a
b
c
d

缺少第5行。

最后一行是如何打印的?我对这个程序的解释在哪里出错?

2 个答案:

答案 0 :(得分:3)

Sed调试可能有点痛苦。有一些有限的内置选项可以帮助您:l命令打印当前模式空间,其中包含打印的换行符,标签和行结尾等不可见字符,以及=(GNU扩展名)打印当前行号。

将这些用于您的示例输入,在N命令之前和之后打印行号和当前模式空间为我们提供了以下信息:

$ sed ':a;$d;=;l;N;=;l;2,5ba;P;D' <<< "$(printf 'a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n')"
1
a$
2
a\nb$
2
a\nb$
3
a\nb\nc$
3
a\nb\nc$
4
a\nb\nc\nd$
4
a\nb\nc\nd$
5
a\nb\nc\nd\ne$
5
a\nb\nc\nd\ne$
6
a\nb\nc\nd\ne\nf$
a
6
b\nc\nd\ne\nf$
7
b\nc\nd\ne\nf\ng$
b
...
<snip>
...
d
9
e\nf\ng\nh\ni$
10
e\nf\ng\nh\ni\nj$
e

查看<snip>之后的部分,我们刚刚打印了d,而D命令已将我们发送回循环的开头,而没有读取新行。我们正在查看第9行,其中包含i(已经位于模式空间的末尾)。

现在我们通过命令:

  • $d - 忽略,我们正在查看第9行,这不是最后一行(请参阅调试输出)
  • N - 追加第10行;调试输出显示已附加j,我们实际上正在查看第10行
  • 2,5ba - 忽略,我们已经过了第5行
  • P - 打印第一行:这是e来自的地方
  • D - 删除第一行,重启周期,不要读下一行

下一个周期:

  • $d - 现在我们正在查看最后一行。删除图案空间,不打印任何内容,我们已完成!

我认为您的分析中的错误是在新周期开始时增加行号,但行号增加的唯一位置是N命令。 (顺便说一下,你最后几行忘记了这一点。)

如果您想更深入地研究sed脚本,可以使用Python编写的sed调试器:sedsed。它对命令最后两行的输出如下所示:

PATT:e\nf\ng\nh\ni$
HOLD:$
COMM::a
COMM:$ d
PATT:e\nf\ng\nh\ni$
HOLD:$
COMM:N
PATT:e\nf\ng\nh\ni\nj$
HOLD:$
COMM:2,5 b a
COMM:P
e
PATT:e\nf\ng\nh\ni\nj$
HOLD:$
COMM:D
PATT:f\ng\nh\ni\nj$
HOLD:$
COMM::a
COMM:$ d

它显示每个命令的保持和模式空间。

示例(10行而不是5行)是Peteris Krumins' site(示例75)中解释的100个sed单行之一,但解释并不是非常详细。同样的原则可以在Eric Pement的sed one-liners中找到(这是“单行解释”的灵感)。

答案 1 :(得分:-1)

您对行号测试的再现无序。该程序会在添加下一行之前检查它是否在最后一行

:a; $d; N; 2,5ba; P; D

根据您的seq 10|sed -f that.sed示例,在第9行,$d未触发,添加第10行,并打印第5行,在窗口中留下第6,7,8,9,10行。 D重新开始循环,只有现在执行$d触发器,因为你已经读完了最后一行。