这是一个 (GNU) sed 解决方案:
$ sed -r '1N;N;/^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/;P;D' infile
3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
这适用于移动的三行窗口。更具可读性:
sed -r ' # -r for extended regular expressions: () instead of \(\)
1N # On first line, append second line to pattern space
N # On all lines, append third line to pattern space
/^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/ # See below
P # Print first line of pattern space
D # Delete first line of pattern space
' infile
N;P;D 是获取移动两行窗口的惯用方式:追加一行、打印第一行、删除模式空间的第一行。为了获得一个移动的三行窗口,我们读取了另外一行,但只读取了一次,即在处理第一行时 (1N)。
复杂的一点是检查模式空间的第一行和第三行是否相同,如果相同,则将第二行替换为第一行。为了检查我们是否必须进行替换,我们使用地址
/^(.*)\n.*\n\1$/
锚点^ 和$ 并不是真正需要的,因为我们在模式空间中总是有精确的换行符,但它更清楚地表明我们想要匹配完整的模式空间。我们将第一行放入一个捕获组,并使用反向引用查看它是否在第三行重复。
然后,如果是这种情况,我们执行替换
s/^(.*\n).*\n/\1\1/
这会捕获包含换行符的第一行,匹配包含换行符的第二行,并用第一行的两倍替换。 P 和 D 然后打印并删除第一行。
当到达结尾时,整个模式空间都会被打印出来,所以我们不会吞下任何行。
这也适用于第二个输入示例:
$ sed -r '1N;N;/^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/;P;D' infile2
2
2
2
2
2
2
要使用 BSD sed(在 OS X 中),您要么必须使用 -E 而不是 -r 选项,要么不使用选项,即基本正则表达式并转义所有括号 ( \(\)) 在捕获组中。换行符匹配应该可以,但我没有测试它。如有疑问,请检查this great answer 列出所有差异。