Keep only rows in a range using vim - vim

I was wondering if there's a way in VIM to keep only rows in a certain range, i.e say I wanted to to keep only rows 1:20 in a file, and discard everything else. Better yet say I wanted to keep lines 1-20 and 40-60 is there a way to do this?
Is there a way to do this without manually deleting stuff?

If you mean entire lines by "rows", just use the :delete command with the inverted range:
:21,$delete
removes all lines except 1-20.
If the ranges are non-consecutive, an alternative is the :vglobal command with regular expression atoms that match only in certain lines. For example, to only keep lines 3 and 7:
:g!/\%3l\|\%7l/delete
There are also atoms for "lines less/larger than", so you can build ranges with them, too.

In order to keep lines 1 through 20 and 40 through 60, the following construct should do:
:v/\%>0l\%<21l\|\%>39l\%<61l/d

If you want to (as I now understand from your comments) save (different) parts of the buffer as new files, it's best to not modify the original file, but to write fragments as separate files. In fact, Vi(m) supports this well, because you can just pass a range to the :write command:
:1,20w newfile1
:40,60w newfile2
Append works, too:
:40,60w >> newfile1

There's not only one way to achieve what you want:
If this question is really about the first rows in a file:
head -20 <filename> > newfile
If it shall be a vim solution:
:21ENTER
dG
However, you mention that you want to split up a large file into smaller pieces. The tool for this is split: it lets you split up files into chunks of even line count or even size.

Related

Vim: find lines with less occurrences of string

In a data file I need to find all lines that contain less than 10 times the pattern |^|
I need them in two ways:
a search, so I can go through the file and examine the data
as a list to be copied, including the next line
I use Gvim in Windows.
So far I've tried along the lines of:
/[|^|]{,9}
/[|^|]*{,9}
:g/\v(\|[^|^|]*){,9}/p
Is anyone able to help me?
Edit: an example (as real data is not allowed to be used)
abc|^|def|^|ghi|^|jkl|^|mno|^|pqr|^|stu|^|vwx|^|yza|^|bcd|^|efg
abc|^|def|^|ghi|^|jkl|^|mno|^|pqr|^|stu|^|vwx|^|yza|^|
bcd|^|efg
abc|^|def|^|ghi|^|jkl|^|mno|^|pqr|^|stu|^|vwx|^|yza|^|bcd|^|efg
Final solution:
:v/\v(\|\^\|.*){10}
I tried this one. I think it will help.
/\(|^|.*\)\{10}
or with \v
/\v(\|\^\|.*){10}

Using diff command, ignore character at end of line

I'm not entirely sure what sort of diff command I'd do to match what I need. But basically I have two different directories full of files that I need to compare and outline the changes of. But in one set of files they basically have a '1' at the end of the line.
An example would be if comparing these two objects
File1/1.txt
I AM IDENTICAL
File2/1.txt
I AM IDENTICAL 1
So I'd just want the diff command to leave out the '1' at the end of the line and show me the files which actually have changes. So far I came up with something like
diff file1/ file2/ -rw -I "$1" | more
but that doesn't work.
Apologies if this is an easy obvious question.
If the number of files and/or size is not that much, you can eyeball the differences and simply use vimdiff command to compare two files side by side
vimdiff File1/1.txt File2/1.txt
Otherwise, as arkascha suggested, first you need to modify your files to eliminate the ending character(s) before comparing them.

How to repeat the same search and replace command over disjunct line ranges in Vim?

I had a situation where I wanted to replace FOO with BAR through out a file. However, I only want to do it in certain places, say, between lines 68–104, 500–537, and 1044–1195. In practice, I dropped markers at the lines of interest (via ma, mb, mc, etc.) and ran the following:
:'a,'b s/FOO/BAR/g | 'c,'d s/FOO/BAR/g | 'e,'f s/FOO/BAR/g
I had to repeat this dozens of times with different search and replace terms s/CAT/DOG, etc., and it became a pain to have to rewrite the command line each time. I was lucky in that I had only three places that I needed to confine my search to (imagine how messy the command line would get if there were 30 or 40).
Short of writing a function, is there any neater way of doing this?
On a related note. I copied FOO to the s (search) register, and BAR to the r (replace) and tried running
:'a,'b s/\=#s/\=#r/ | 'c,'d s/\=#s/\=#r/ | 'e,'f s/\=#s/\=#r/
This would have saved me having to rewrite the command line each time, but, alas, it didn’t work. The replace bit \=#r was fine, but the \=#s bit in the search pattern gave me an error.
Any tips would be appreciated.
If you need to perform a set of line-wise operations (like substitutions) on a bunch of different ranges of lines, one trick you can use is to make those lines look different by first adding a prefix (that isn't shared by any of the other lines).
The way I usually do this is to indent the entire file with something like >G performed on the first line, and then use either :s/^ /X/ commands or block-visual to replace the leading spaces with X on the lines I want.
Then use :g in conjunction with :s. eg:
:%g/^X/s/FOO/BAR/g
:%g/^X/s/BAZ/QUUX/g
Finally, remove the temporary prefixes.
In order to get rid of the necessity to retype the same search
pattern, substitution string and flags, one can simply use the
:& command with the & flag:
:'a,'bs/pat/str/g | 'c,'d&& | 'e,'f&&
(See :help :& for details.)
Instead of using marker use this one :
:68,104s/FOO/BAR/g << substitue from line 68 to 104
This should make your job a little bit easier and clearer.
inspired by #Vdt's answer:
I am not sure but you could write all the substitutions down in a file and source that file i think.
substitutions.vim:
68,104s/FOO/BAR/g
168,204s/FOO/BAR/g
618,644s/FOO/BAR/g
681,1014s/FOO/BAR/g
.
.
.
68,104s/BAZ/BOOO/g
168,204s/BAZ/BOOO/g
and then :so substitutions.vim maybe you can also use this for multiple files of same structure. you can add an e to add an ignore error message, if it is not clear that the substitutions are found on the corresponding line blocks.
With q:, you can recall previous command lines and edit them as a normal Vim buffer, so you can quickly replace FOO and BAR with something else, then re-execute the line with Enter.
The s/\=#s/\=#r/ doesn't work; as you said, this only works in the replacement part. But for the pattern, you can use Ctrl + R Ctrl + R s to insert the contents of register s, instead of \=#s. Preferably use the default register, then it's a simple s//, but you probably know that already.
When performed over a closed fold, substitutions are limited to that fold.
fold each region
put the cursor on one closed fold
perform the substitution: :s/foo/bar<CR>
move to the next closed fold with zj or zk
use the command-line history: :<C-p><CR> or :<Up><CR> to perform the same substitution
repeat…
You can also add the c flag at the end of your substitution so that Vim asks you for a confirmation before actually performing it. This can be tedious if you have lot of matches.
Here's the simplest way to do it
:5,10s/old/new/g
5,10 : startlinenum,endlinenum

How can I separate pages with Vim?

I have the requirement of separating an ASCII document into pages of max length 58 lines per page. At the bottom of each page there is a 3 line footer. I'm not aware of any pagination abilities within Vim that would accomplish this.
Is there a good way to do this with Vim? Perhaps highlighting every 58th line or something of the sort.
N.B. I'm seeing answers involving using a separate tool to do this; which I have thought of. What I'm interested in is a Vim solution.
Thanks.
The proper tool you're looking for is very likely a2ps.
a2ps --lines-per-page 58 --footer=footer_text document.txt
It's possible in vim as a script. Put the following in a file and :source it while the file to change is open. The s:footer list are the lines to insert after each run of 58 lines.
let s:footer = ["","Footer",""]
let s:line = 0
while s:line <= line("$") - 58
let s:line = s:line + 58
call append(s:line, s:footer)
let s:line = s:line + len(s:footer)
endwhile
Why is it important to use vim? You could use split and cat more efficiently.
Assuming your original file is called file and you have a file, footer, created that includes your footer text.
$ split -l 58 file file_parts
$ for i in file_parts*; do cat $i footer > $i.footered; done
$ cat file_parts*.footered > file.footered
file.footered would have your original file with the contents of footer inserted at every 58th line.
This is assuming you want it all back in the original file. If you don't, then the resulting file_parts*.footered files would be the already separated pages so you could skip the last step.
The two most effective ways for doing that in Vim are a script (like
#Geoff has already suggested) and a substitution command, like
:%s#\%(.*\n\)\{58}#\0---\rfooter\r---\r#
A macro (as suggested in a comment to the question) is the slowest,
a script is the fastest. A substitution command is slower than script,
but much faster than a macro.
So probably substitution is the best Vim-only solution unless its
performance is unacceptable. Only in that case, I think, it is worth
writing a script.
You're probably trying to use the wrong tool for this. You could do it much easier programmatically, for example with this simple Perl oneliner:
perl -pe'print "your\nfooter\nhere\n" unless $. % 58' inputfilename > outputfilename
A recursive macro might work. Experiment with the following (position the cursor on the first character of the first line and switch to normal mode):
qqq
qq
57j
:read footer.txt
3j
#q
q
Note that the register to which you record the macro must be cleared (qqq) and that you must not use tab-completion when reading the footer-file (:read footer.txt).
You can then use the macro (normal mode):
#q

vim -- How to read range of lines from a file into current buffer

I want to read line n1->n2 from file foo.c into the current buffer.
I tried: 147,227r /path/to/foo/foo.c
But I get: "E16: Invalid range", though I am certain that foo.c contains more than 1000 lines.
:r! sed -n 147,227p /path/to/foo/foo.c
You can do it in pure Vimscript, without having to use an external tool like sed:
:put =readfile('/path/to/foo/foo.c')[146:226]
Note that we must decrement one from the line numbers because arrays start from 0 while line numbers start from 1.
Disadvantages: This solution is 7 characters longer than the accepted answer. It will temporarily read the entire file into memory, which could be a concern if that file is huge.
The {range} refers to the destination in the current file, not the range of lines in the source file.
After some experimentation, it seems
:147,227r /path/to/foo/foo.c
means insert the contents of /path/to/foo/foo.c after line 227 in this file. i.e.: it ignores the 147.
Other solutions posted are great for specific line numbers. It's often the case that you want to read from top or bottom of another file. In that case, reading the output of head or tail is very fast. For example -
:r !head -20 xyz.xml
Will read first 20 lines from xyz.xml into current buffer where the cursor is
:r !tail -10 xyz.xml
Will read last 10 lines from xyz.xml into current buffer where the cursor is
The head and tail commands are extremely fast, therefore even combining them can be much faster than other approaches for very large files.
:r !head -700030 xyz.xml| tail -30
Will read line numbers from 700000 to 700030 from file xyz.xml into current buffer
This operation should complete instantly even for fairly large files.
I just had to do this in a code project of mine and did it this way:
In buffer with /path/to/foo/foo.c open:
:147,227w export.txt
In buffer I'm working with:
:r export.txt
Much easier in my book... It requires having both files open, but if I'm importing a set of lines, I usually have them both open anyway. This method is more general and easier to remember for me, especially if I'm trying to export/import a trickier set of lines using g/<search_criteria/:.w >> export.txt or some other more complicated way of selecting lines.
You will need to:
:r /path/to/foo/foo.c
:d 228,$
:d 1,146
Three steps, but it will get it done...
A range permits a command to be applied to a group of lines in the current buffer.
So, the range of read instruction means where to insert the content in the current file, but not the range of file that you want to read.

Resources