我在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行。
最后一行是如何打印的?我对这个程序的解释在哪里出错?
答案 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
触发器,因为你已经读完了最后一行。