0

Requirements

In a batch manner, i.e., with ex, vim -e or vim -E, I want to print all lines between line b1 and line b2, boundaries included.

b1 is:

  • The first line with a "sta" match, if present.
  • The first line of the file, otherwise.

b2 is:

  • The last line with an "end" match, if present.
  • The last line of the file, otherwise.

If b1 comes after b2, the result is unspecified.

Attempts

$ ex file <<< '/sta/;?end?p'

obviously fails if either "sta" or "end" is not missing.


$ ex file <<EOF
?end?
ke 
1
/sta/
ks
's,'ep
EOF

looks good, but if "sta" is in the first line of the file, the line is deleted (likewise for "end" being in the last line).

A test file

You can try your solution on the following file. The output should contain all lines between the <--- markers.

111
sta <---

sta rrr
def ghi jkl
jkl end
end <---
999

Removing 111 and/or 999 should give you the same output.

Then remove all occurrences of sta and then of end to see if it respects the fallback requirements.

My motivation for this question is another question in Unix & Linux. You may well as go there and answer it also, but the requirements are slightly different.

0
1

Those requirements make relying solely on pattern-ranges tricky...perhaps even impossible though I didn't spend too much time thinking about it. Instead I figured I'd go the KISS route and use a search and mark approach and then use the marks as a range to be printed.

I'll lay it all out on individual lines:

  • call search('sta', 'c') : search forward for 'sta', cursor unmoved if not found
  • mark t : mark first occurrence of 'sta' if found otherwise mark line 1

Next, we do the same thing but in reverse: start at last line and search backwards...

  • call cursor('$', 999) : go to last column of last line
  • call search('end', 'cb') : b flag to search backwards
  • mark b

Finally, we print...

  • 't,'bp

Note that if t(op) comes after b(ottom), such as if 'end' comes before 'sta', nothing is printed.

Here it is in a Vim command line (using abbreviations to reduce the size). Note the addition of flag +1 which is needed to put the cursor on line 1.

vim -e +1 +"cal search('sta', 'c') | kt | cal cursor('$', 999) | cal search('end', 'cb') | kb | 't,'bp" /dev/stdin <filename

A bit ugly, yes. But it's easy to understand and it works (assuming I read everything correctly).

0
0

Not sure I follow. Is there anything wrong with the following approach:

ex testfile <<EOF
1?end?
ke
$/sta/
ks
's,'ep
EOF

The trick is to position the cursor at the end line, so that searching forwards will find the match at the beginning of the file. Same for searching backwards, position the cursor at the first line and then search backwards.

1
  • 1
    AFAIUI, the printed range starts at line 1 if 'sta' is not present and ends at last line if 'end' isn't present. Thus entire file is printed if neither are present...I think this just prints the last line in that case. (I didn't know about k...nice!)
    – B Layer
    Sep 14 '21 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.