Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
551 views
in Technique[技术] by (71.8m points)

regex - How does :g/^$/,/./-j (reduce multiple blank lines to a single blank) work in vim?

In the article, Vim Regular Expressions, Oleg Raisky gives the following command to reduce multiple blank lines to a single blank:

:g/^$/,/./-j

Can someone please describe how this works?

I know :g command and regular expressions. But I didn't understand what the part /,/./-j does.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It really is quite ingenious. Let's break it down. The ex command

g/^$/xyzzy

will search for all empty lines and execute the xyzzy command (an arbitrary ex command) on each of them.

The tricky bit here is that the xyzzy command in your case is yet another substitute command:

,/./-j

The ,/./- specifies a range. This is of the form <start>,<end> and, because there's nothing before the comma, it assumes the current line (the one where you found the blank line) is the start.

After the comma is /./- which means search for the next character (. means any character) then back up one line (/./- is short for /./-1 since the one is implied if no value is given). You'll find that pattern . on the first non-blank line following the one you're operating on.

In other words, the end of the range is the last blank line after or at the one you're currently operating on.

Then you execute a join over that range.

If the start and the end of the range were equal (only one blank line was in the section), join does nothing. If they're not equal, join will join them all up.

That's the way in which it combines multiple blank lines into one.

Lets look at an example (line numbers are not in the file):

1 Line 1
2
3 Line 3
4 Line 4
5
6
7
8
9 Line 9

The :g command will find all blank lines and perform its operation on them (lines 2, 5, 6, 7 and 8).

For line 2, ,/./-j will set up a range from 2 to 2 (next . found on line 3 then subtract 1). A join on the range 2,2 does nothing.

For line 5, ,/./-j will set up a range from 5 to 8 (next . found on line 9 then subtract 1). A join on the range 5,8 will join all those lines together.

I'm not entirely certain about this but I think the operation may not be performed on lines that disappear as part of an earlier operation. That's because it would make no sense to process lines that have been deleted earlier in the cycle.

In other words, because lines 6 through 8 are deleted (combined with line 5), the global command doesn't operate on them after that. I base that on nothing more than the fact that the vim documentation states a two-pass algorithm, one to mark the lines, one to perform the operation.

I may be wrong on that point (it wouldn't be the first time) but it's an implementation detail which doesn't affect the functionality.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...