24

I have a text file like this (using gVim on Windows)

foo bar baz quux 
corge grault garply 
waldo fred plugh 
[...150 more lines...]
xyzzy thud

I want to add a number to each line in the file. Not using :set number, but to add the number as text preceding each line, as follows, so the number is part of the file.

1. foo bar baz quux 
2. corge grault garply 
3. waldo fred plugh 
[...~150 more lines...]
155. xyzzy thud
  • awk is probably the tool for this job. But I'm on Windows (sigh). – roblogic Feb 11 '15 at 11:11
  • Answered here already, never mind :) – roblogic Feb 11 '15 at 11:20
  • 1
    Perhaps.. Or is this more general? – muru Feb 14 '15 at 22:25
  • It's similar, but I wouldn't know what are permanent line numbers. Secondly the other question is about all lines (and the answer does that) for gVim on Windows specifically and this is simple numbered list for one paragraph only in plain vim. – kenorb Feb 14 '15 at 22:42
  • 3
    Well, I guess that post uses "permanent" ot indicate that the buffer is to be modified, and that the numbers aren't some purely visual thing (same as you). The reason for specifying gvim on Windows is to avoid external utilities like cat or nl, which can do number lines, but aren't generally available on Windows (as OP indicates from their comment about awk). The top two solutions are pure Vim. Lastly, all lines vs one para is just a matter of range selection. Clearly not a big issue. – muru Feb 14 '15 at 22:45
41

In pure Vim fashion:

:%s/^/\=line('.').". "

Explanation:

:%s/^/            " the substitution will be applied to the beginning of every line
\=                " the rest of the replacement part is an expression
line('.').". "    " the expression returns the current line number concatenated with a dot and a space

See :help \= and :help line().

Using an expression in the replacement part is very powerful and FWIW a pretty good point of entry to vimscript.

| improve this answer | |
  • How can I add this very useful command to a keymap in vimrc? – cosmicraga Jul 20 '19 at 8:26
  • 1
    To get to the vim help topic for substitution: :help sub-replace-expression – akurtser Sep 1 '19 at 13:00
9

One nice thing about Vim macros is that they can recurse (they can invoke themselves):

  1. Clear out register q: qqq
  2. Add the number to the first line: ggI1. (don't forget the space!)
  3. Move back to start of line and start recording a macro: 0qq
  4. Copy the number: yW
  5. Move down a line and paste the number: +P
  6. Move back to the start of the line and increment the number: 0<c-a>
  7. Move back to the start of the line (so the macro doesn't break when it gets to double figures!): 0
  8. Call the macro once, to make it recursive. At this point, there is still nothing in register q, so nothing will happen: @q.
  9. Save the macro: q
  10. Call the macro one more time, and watch the sparks fly!: @@

The macro will then continue invoking itself until it reaches the end of the file.

You can use the recursive macro trick for lots of other similar problems, so it's a good one to be aware of.

If you don't want to use a recursive macro for some reason, you can omit steps 1 and 8, and use a count to run the macro multiple times, e.g. 100@q will run macro q 100 times.

| improve this answer | |
  • 1
    Powerful stuff, I bow to your mastery. Macros are like black magic to me... – roblogic Feb 11 '15 at 12:09
  • 1
    @ropata, a macro is only a sequence of (mostly) normal mode commands. – romainl Feb 11 '15 at 13:37
  • 1
    @romainl I think it's better to think of it as a sequence of keystrokes. – Rich Feb 11 '15 at 13:47
  • 2
    @Rich, it can be a sequence of many things, including ex commands. – romainl Feb 11 '15 at 21:34
  • 2
    @romainl Yes, that's why I think it's best to think of it as keystrokes. It plays back exactly what you type on the keyboard (including, as you say, ex commands), as if you had typed it all in manually. – Rich Feb 12 '15 at 9:46
7

I like to use the vim global command to accomplish tasks like this. This applies to adding iteration to the beginning of a line or modify a symbol in the text. It looks more complicated than the other solutions, but is a pretty flexible pattern to use when you have it handy, and is easy to modify without a lot of thought.

First, pick your range (which lines you want to apply this). I usually use marks (e.g. ma on the first line and mb on the second, but you can also use line numbers or visual selection), then enter a modification of the following command (currently tweaked for your use case)

:let i=1|'a,'bg/^/s/^/\=i.". "/|let i=i+1

Deconstruction

:let i=1

This sets up variable i with a start value. Usually lists start with 1, so I'm setting i to 1.

|

The bar starts a new command

'a,'b

This sets the range of the next command. I'm going from mark a to mark b, which would be set on the first line and last line of your list.

g/^/

This is the global command. It searches the file (or range) for a given regular expression, and will execute the rest of the command line on each of the lines that matched. I'm matching every line by searching for "beginning of line". If you had text like

Item some txt
other text

Item second item
whatever
Item third

and only want to put these labels in front of Item and ignore the other lines, do g/Item/ or g/^Item/ instead (assuming the literal Item text)

s/^/\=i.". "/

This executes the regular expression to replace the beginning of the line with the value of i concatenated with a .. Generally you can do this to anything (replace the label Item with the number, for example).

|let i=i+1

Even though the bar starts a new command, it sets up a second command to run within the global command, instead of after the global is complete. The result is we increment i before the next line is processed by g. Here is another place of flexibility. The modification of i can be anything (increment by 2, call a function that generates the next element of the Fibonacci sequence, whatever).

| improve this answer | |
7

Add numbers to all lines

It's possible to use :%!nl -ba or :%!cat -n commands which will add line numbers to all the lines.

On Windows, you've to have Cygwin/MSYS/SUA installed.

Add numbers to selected lines

To add numbers only for selected lines, please select them in visual mode (v and cursors), then when finished - execute the command: :%!nl (ignore blank lines) or :%!cat -n (blank lines included).

Formatting

To remove extra spaces, select them in visual block (Ctrl+v) and remove them (x).

To add some characters (., :, )) after the numbers, select them in visual block (Ctrl+v), then append the character (A, type the character, then finish with Esc).

| improve this answer | |
  • 2
    This does not give the same formatting as given in the question. However, I like the simplicity of the solution. – Karl Yngve Lervåg Feb 15 '15 at 9:27
  • @KarlYngveLervåg Thanks, included that in the answer. – kenorb Feb 15 '15 at 22:12
5

A modification of romainl's answer:

:%s/^\(\d\+\. \)\?/\=line('.').". "

This will not just add line numbers, it will also replace exiting line numbers it they're already there. Of, if you've inserted a line mid-way, it will renumber everything as expected.

This works by replacing any number followed by a . and a space at the start of the line with a new number. This will obviously break if you have a line that already starts with this pattern, so use with thought.

The added part:

  • ^ - Start of line
  • \( - Start new subgroup
  • \d\+ - Match a digit once or more
  • \. - Match a dot (.) and a space .
  • \) - End subgroup
  • \? - Make the group optional, so that it works as before if there's no number on this line yet.

Bonus hint:
To remove the line numbers, you can use the same pattern with the repalce part empty:

:%s/^\(\d\+\. \)\?//
| improve this answer | |
5
I1. <esc>^qqyWjP^<C-a>q

This numbers the first two lines, and you can press @q to number subsequent lines (or type ex. 18@q if you want to number 20 lines total).

Explanation:

I1. <esc>  Number the first line
hqq        Go back to the start of the line and start recording a macro
yWjP       Copy the line number to the next line
^<C-a>     Increment the next line's line number
q          Finish recording

The benefit of this is that it doesn't require any external commands, which is useful if you're working with Vim on Windows, for example.

| improve this answer | |
  • After typing 1. <esc>h you are on the second column, not first column. I would replace the h with 0, after which I think your solution should be very good. – Karl Yngve Lervåg Feb 15 '15 at 9:26
  • @KarlYngveLervåg Whoops, that was a typo. Thanks, fixed it. – Doorknob Feb 15 '15 at 14:18
  • No problem. However, you still have not updated the explanation. Also: On many keyboards, ^ waits for a second character in order to allow typing combinations like ^a -> â. I still agree its the best solution, but I think this should be mentioned as well. – Karl Yngve Lervåg Feb 16 '15 at 10:48
3

I think the chosen answer is the best, but in the sprit of variety, I'll offer an alternative using an external program:

:%!cat -n

This will filter your entire buffer (as denoted by %) through the external program, cat, where the -n flag prepends each line of input with a line number.

This, of course, requires that you have cat installed, which is true for (probably) all Unix-like systems.

Check out :help :range! for more details on filtering through external programs.

| improve this answer | |
  • 1
    I realize that the asker is using gVim on Windows, so this solution probably won't work there. However, I think it still provides some opportunity for others to learn from it. – tommcdo Feb 11 '15 at 14:28
  • If you've installed msysgit and added that to your PATH (IIRC it's an install option), this solution should also work on Windows. – Martin Tournoij Feb 11 '15 at 14:40
  • 4
    cat -n is not POSIX, but nl is, so it might be a better option. – muru Feb 11 '15 at 14:43
2

A bit hackish solution might be the following (everything written between < and > are to be inserted after pressing Ctrl+v):

:%normal :redir @"<Enter>:-=<Enter>:redir END<Enter>I<C-R>".<Tab><Esc>kdd

Deconstruction

:%normal {commands}

runs normal mode command on each line specified by the range, in this case every line

:redir @"

redirects every output made by ex commands to the unnamed buffer.

:.=

is an ex command that outputs the current line number (with a preceding newline unfortunately)

:redir END

stops redirecting to the unnamed buffer

I<C-R>".<Tab><Esc>

inserts the content of the unnamed buffer with a . and a tab to to front of each line and exits from insert mode.

kdd

goes one line up and removes the newline which is the result of the :.= command.

| improve this answer | |

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.