How to split the first 2 columns from the line in vim? - vim

xyz mnl pqt aaaa ccc
yz mn ats aa cbc ddd eee ggg
I have a file with each line like the above. Each column is split with a space. I want to remove all other columns except the first 2 columns, namely:
xyz mnl
yz mn
I tried a number of patterns but they don't work.

For a more Unix-y solution:
:%!awk '{print $1, $2}'
Where:
:%!<cmd> filters every line in the buffer through external command <cmd>.
See :help :range and :help :!.
awk '{print $1, $2}' is that external command.
'{print $1, $2}' is a short AWK script that prints the first and second fields of each line.
For more information on awk: $ man awk.

One fairly easy way to do it is to use the command :normal with a Normal mode command that would delete anything past the second field. For example, you could use 2f<Space> to seek to the second space, followed by D (or the equivalent d$) to delete until the end of the line.
You can then use that command with a range, for example :% to perform it on the whole buffer, or perhaps select the lines with a Visual selection and then when you start the Ex command with :, Vim will automatically insert :'<,'> which means the lines in the Visual selection.
So, for example, performing the substitution on the whole buffer:
:%norm 2f D
See :help :normal for more details.

Using the command line
:%norm wwhD

If a line in the file separated by a single whitespace character, then you can simply done the job using :substitute command:
:%s/^\S\+ \S\+\zs.*$//
The point here is that \zs to start matching columns you want to remove.
With \zs you don't need to save the first 2 words with \(\S\+ \S\+\).

Related

A custom function in Vim

I'm quite new to Vim/Vi, and I need to write a custom function/macro.
Is it possible to define a command (ex. :mycommand) that would format the lines in the current file like so:
Initial lines:
This is line 1
This is line 2
This is line 3
This is line 4
This is line 5
This is line 6
This is line 7
This is line 8
Result:
This is line 1\nThis is line 2
This is line 3\nThis is line 4
This is line 5
This is line 6\nThis is line 7\nThis is line 8
How do I go about creating such a script? And where do I place it?
Marco
You can record a macro, to join two lines by \n separator, for example in your vimrc:
let #x='gJi\n^['
(the ^[ above, you press ctrl-v Esc)
Assume your cursor is on the first line, you can do #x in normal mode, then )J## for the 2nd block, then job is done.
You can wrap those operations in function or create them as mapping.
Join plugin
I have written a Join script: https://github.com/sk1418/Join , it supports to join lines with separator and other features, in your case, you can execute command: :J '\n', it will do what above macro (#x) does. You can put it in your function too, like:
function Foo()
Join '\n'
join!
Join '\n'
endfunction
Update for the Question modification:
The modification you made, turned the question into another one... However it could be solved, I listed two possibilities below, one is vim way, the other is with external awk tool, if you have awk available on your system.
with vim :s cmd
This command should do it for you:
%s/\n\n/∢/g|%s/\n\ze./\\n/g|%s/∢/\r/g
the ∢ is done by pressing ctrl-v u2222, it is just for a special char, which not exists in your text, you can use other uni-code chars too.
with external awk
%!awk -v RS='\n\n' '{gsub(ORS, "\\n")}7'
This will do the transformation for you, however it leaves an extra \n at the end of the file, just remove it.

Search and append text at the end of the line

I have a file in which there is a command on each line. Within the command there is a line which is always P0......xml. I want to append that P0.... to the end of each line with a >> operator in front and .log at the end.
For example,
If a command looks like this:
abcdefg hijkl mno P0qrstuv.xml wxyz abc
I would like to make it:
abcdefg hijkl mno P0qrstuv.xml wxyz abc >> P0qrstuv.log
Please explain your answer as precisely as possible.
One solution would be to use the substitute command in Ex mode. This should do it.
:%s/\v(P0.+\.xml).*/& >>\1.log/
: puts you into Ex mode
% act on the whole file
s does the substitution
\v specifies that the regex will "very magic" and not the regular vim regex.
(P0.+\.xml) grouping of text the file name
& inserts what was matched
\1 inserts the first grouping (if you would have more than one you could do \2 \3 etc..)

How to let Vim process my command backforward?

Suppose I have 5 lines of text, if I input some commands to let vim process each line, Vim will process the text one by one, first line, second line, ... the last line. what I want is to let Vim process my text in reverse order. that is the last line first, then the 4th line, and at last the first line.
Why I need this?
I have the following text
1234567890
abc
123
def
1234567890
123456789
I want to remove the newline symbol(\n) from lines which contains 3 characters. so after processing,I will get the following text
1234567890
abc123def1234567890
123456789
It seems a piece of cake, I use the following command
:%s/\v(^\w{3})\n/\1/
But what i got is
1234567890
abc123
def1234567890
1234567890
Why? I guess vim first remove \n from the second line, then this line has text abc123, now vim will not remove \n after 123, since it's not 3 characters now, so vim process the next line def, and remove \n from it, that's the result i got.
If vim can process from back to front, this will not happen and I can got the result I want.
BTW, I can get the expected result in other ways, I just want to know whether this is possible.
Explicitly loop over the range of lines (e.g. the visually selected ones) backwards, then execute the command on each line. I've used :join here instead of the :substitute with a newline:
:for i in range(line("'>"), line("'<"), -1)| silent execute i . 'g/^\w\{3}$/join!' | endfor
Can be achieved using perl:
while (<>) {
chomp if (/^...$/);
print;
}
In this case it is easier to use the :global command to join the lines.
:g/^\w\{3}$/normal! gJ
The command gJ joins the current line with the following line without inserting any spaces. The global command above calls gJ on each line containing only three characters. It works by marking all the matches first, before performing the operation, so the problem of looping is avoided.
this line should do what you want:
:%s/\v(\_^\w{3}\n)+/\=substitute(submatch(0),"\n","","g")/
if you want to do it simpler with external command, e.g. awk, you could:
%!awk '{printf "\%s", length($0)==3? $0:$0"\n"}'

Combine matching lines using sed or awk?

I have a file like the following:
1,
cake:01351
12,
bun:1063
scone:13581
biscuit:1931
14,
jelly:1385
I need to convert it so that when a number is read at the start of a line it is combined with the line beneath it, but if there is no number at the start the line is left as is. This would be the output that I need:
1,cake:01351
12,bun:1063
scone:13581
biscuit:1931
14,jelly:1385
Having a lot of trouble achieving this with sed, it seems it may not be the best way for what I think should be quite simple.
Any suggestions greatly appreciated.
A very basic sed implementation:
sed -e '/^[0-9]/{N;s/\n//;}'
This relies on the first character on only the 'number' lines being a number (as you specified).
It
matches lines starting with a number, ^[0-9]
brings in the next line, N
deletes the embedded newline, s/\n//
This is a file on my intranet. I can't recall where I found the handy sed one-liner. You might find something if you search for 'sed one-liner'
Have you ever needed to combine lines of text, but it's too tedious to do it by hand.
For example, imagine that we have a text file with hundreds of lines which look like this:
14/04/2003,10:27:47,0
IdVg,3.000,-1.000,0.050,0.006
GmMax,0.011,0.975,0.005
IdVg,3.000,-1.000,0.050,0.006
GmMax,0.011,0.975,0.005
14/04/2003,10:30:51,600
IdVg,3.000,-1.000,0.050,0.006
GmMax,0.011,0.975,0.005
IdVg,3.000,-1.000,0.050,0.006
GmMax,0.010,0.975,0.005
14/04/2003,10:34:02,600
IdVg,3.000,-1.000,0.050,0.006
GmMax,0.011,0.975,0.005
IdVg,3.000,-1.000,0.050,0.006
GmMax,0.010,0.975,0.005
Each date (14/04/2003) is the start of a data record, and it continues on the next four lines.
We would like to input this to Excel as a 'comma separated value' file, and see each record in its own row.
In our example, we need to append any line starting with a G or I to the preceding line, and insert a comma, so as to produce the following:
14/04/2003,10:27:47,0,IdVg,3.000,-1.000,0.050,0.006,GmMax,0.011,0.975,0.005,IdVg,3.000,...
14/04/2003,10:30:51,600,IdVg,3.000,-1.000,0.050,0.006,GmMax,0.011,0.975,0.0005,IdVg,3.000,...
14/04/2003,10:34:02,600,IdVg,3.000,-1.000,0.050,0.006,GmMax,0.011,0.975,0.0005,IdVg,3.000,...
This is a classic application of a 'regular expression' and, once again, sed comes to the rescue.
The editing can be done with a single sed command:
sed -e :a -e '$!N;s/\n\([GI]\)/,\1/;ta' -e 'P;D' filename >newfilename
I didn't say it would be obvious, or easy, did I?
This is the kind of command you write down somewhere for the rare occasions when you need it.
Try a regular expression, such as:
sed '/[0-9]\+,/{N}s/\n//)'
That checks the first line for a number (0-9) and a comma, then replaces the new line with nothing, removing it.
Another awk solution, less cryptic than some other answers:
awk '/^[0-9]/ {n = $0; getline; print n $0; next} 1'
$ awk 'ORS= /^[0-9]+,$/?" ":"\n"' file
1, cake:01351
12, bun:1063
scone:13581
biscuit:1931
14, jelly:1385

How to add line numbers to range of lines in Vim?

How can I add line numbers to a range of lines in a file opened in Vim? Not as in :set nu—this just displays line numbers—but actually have them be prepended to each line in the file?
With
:%s/^/\=line('.')/
EDIT: to sum up the comments.
This command can be tweaked as much as you want.
Let's say you want to add numbers in front of lines from a visual selection (V + move), and you want the numbering to start at 42.
:'<,'>s/^/\=(line('.')-line("'<")+42)/
If you want to add a string between the number and the old text from the line, just concatenate (with . in VimL) it to the number-expression:
:'<,'>s/^/\=(line('.')-line("'<")+42).' --> '/
If you need this to sort as text, you may want to zero pad the results, which can be done using printf for 0001, 0002 ... instead of 1, 2... eg:
:%s/^/\=printf('%04d', line('.'))/
Anyway, if you want more information, just open vim help: :h :s and follow the links (|subreplace-special|, ..., |submatch()|)
cat -n adds line numbers to its input. You can pipe the current file to cat -n and replace the current buffer with what it prints to stdout. Fortunately this convoluted solution is less than 10 characters in vim:
:%!cat -n
Or, if you want just a subselection, visually select the area, and type this:
:!cat -n
That will automatically put the visual selection markers in, and will look like this after you've typed it:
:'<,'>!cat -n
In order to erase the line numbers, I recommend using control-v, which will allow you to visually select a rectangle, you can then delete that rectangle with x.
On a GNU system: with the external nl binary:
:%!nl
With Unix-like environment, you can use cat or awk to generate a line number easily, because vim has a friendly interface with shell, so everything work in vim as well as it does in shell.
From Vim Tip28:
:%!cat -n
or
:%!awk '{print NR,$0}'
But, if you use vim in MS-DOS, of win9x, win2000, you loss these toolkit.
here is a very simple way to archive this only by vim:
fu! LineIt()
exe ":s/^/".line(".")."/"
endf
Or, a sequence composed with alphabet is as easy as above:
exe "s/^/".nr2char(line("."))."/"
You can also use a subst:
:g/^/exe ":s/^/".line(".")."^I/"
You can also only want to print the lines without adding them to the file:
"Sometimes it could be useful especially be editing large source files to print the line numbers out on paper.
To do so you can use the option :set printoptions=number:y to activate and :set printoptions=number:n to deactivate this feature.
If the line number should be printed always, place the line set printoptions=number:y in the vimrc."
First, you can remove the existing line numbers if you need to:
:%s/^[0-9]*//
Then, you can add line numbers. NR refers to the current line number starting at one, so you can do some math on it to get the numbering you want. The following command gives you four digit line numbers:
:%!awk '{print 1000+NR*10,$0}'
The "VisIncr" plugin is good for inserting columns of incrementing numbers in general (or letters, dates, roman numerals etc.). You can control the number format, padding, and so on. So insert a "1" in front of every line (via :s or :g or visual-block insert), highlight that column in visual-block mode, and run one of the commands from the plugin.
If someone wants to put a tab (or some spaces) after inserting the line numbers using the this excellent answer, here's a way. After going into the escape mode, do:
:%s/^/\=line('.').' '/
^ means beginning of a line and %s is the directive for substitution. So, we say that put a line number at the beginning of each line and add 4 spaces to it and then put whatever was the contents of the line before the substitution, and do this for all lines in the file.
This will automatically substitute it. Alternatively, if you want the command to ask for confirmation from you, then do:
:%s/^/\=line('.').' '/igc
P.S: power of vim :)
The best reply is done in a duplicate question.
In summary:
with CTRL-V then G I 0 You can insert a column of zero.
Then select the whole column and increment:
CTRL-V g CTRL-A
See also: https://vim.fandom.com/wiki/Making_a_list_of_numbers#Incrementing_selected_numbers

Resources