在这里找到That's What I Sed(比this solution 慢)。
sed '/^$/N;/\n$/D' file
sed 脚本可以这样解读:
如果下一行为空,则删除当前行。
并且可以翻译成如下伪代码(对于已经熟悉sed的读者,buffer指的是模式空间):
1 | # sed '/^$/N;/\n$/D' file
2 | while not end of file :
3 | buffer = next line
4 | # /^$/N
5 | if buffer is empty : # /^$/
6 | buffer += "\n" + next line # N
7 | end if
8 | # /\n$/D
9 | if buffer ends with "\n" : # /\n$/
10 | delete first line in buffer and go to 5 # D
11 | end if
12 | print buffer
13 | end while
在正则表达式/^$/ 中,^ 和$ 符号分别表示“缓冲区的开始”和“缓冲区的结束”。它们指的是缓冲区的边缘,而不是缓冲区的内容。
D 命令执行以下任务:如果缓冲区包含换行符,则删除缓冲区的文本直到第一个换行符,并重新启动程序循环(返回第 1 行)而不处理其余命令,不打印缓冲区,也不读取新的输入行。
最后,请记住sed 在处理该行之前会删除尾随换行符,并请记住print 命令会添加回尾随换行符。所以,在上面的代码中,如果要处理的下一行是Hello World!\n,那么next line隐含地引用Hello World!。
更多详情请访问https://www.gnu.org/software/sed/manual/sed.html。
您现在已准备好将算法应用于以下文件:
a\n
b\n
\n
\n
\n
c\n
现在让我们看看为什么this solution 更快。
sed 脚本/^$/{:a;N;s/\n$//;ta} 可以这样解读:
如果当前行匹配/^$/,则执行{:a;N;s/\n$//;ta}。
由于^ 和$ 之间没有任何内容,我们可以这样改写:
如果当前行为空,则执行{:a;N;s/\n$//;ta}。
表示sed对每个空行执行以下命令:
| Step |
Command |
Description |
| 1 |
:a |
Declare a label named "a". |
| 2 |
N |
Append the next line preceded by a newline (\n) to the current line. |
| 3 |
s/\n$// |
Substitute (s) any trailing newline (/\n$/) with nothing (//). |
| 4 |
ta |
Return to label "a" (to step 1) if a substitution was performed (at step 3), otherwise print the result and move on to the next line. |
非空行按原样打印。知道了这一切,我们可以用下面的伪代码来描述整个过程:
1 | # sed '/^$/{:a;N;s/\n$//;ta}' file
2 | while not end of file :
3 | buffer = next line
4 | # /^$/{:a;N;s/\n$//;ta}
5 | if buffer is empty : # /^$/
6 | :a # :a
7 | buffer += "\n" + next line # N
8 | if buffer ends with "\n" : # /\n$/
9 | remove last "\n" from buffer # s/\n$//
10 | go to :a (at 6) # ta
11 | end if
12 | end if
13 | print buffer
14 | end while
如您所见,两个sed 脚本非常相似。事实上,s/\n$//;ta 与/\n$/D 几乎相同。但是,第二个脚本跳过了第 5 步,因此它可能比第一个脚本更快。让我们用大约 10Mb 的空行来计算两个脚本的时间:
$ yes '' | head -10000000 > file
$ /usr/bin/time -f%U sed '/^$/N;/\n$/D' file > /dev/null
3.61
$ /usr/bin/time -f%U sed '/^$/{:a;N;s/\n$//;ta}' file > /dev/null
2.37
第二个脚本获胜。