I am trying to write a command that changes author lists of the form

R. L. Rivest, A. Shamir, and L. Adleman

Into one nice for BibTeX, namely:

Rivest, R. L. and
Shamir, A. and
Adleman, L

I have figured out how to do it in two commands:

" This command will split the names so they are one per line, noting that
" sometimes the author list may end in ', and' or 'and ' or just ', '
command! -range=% Namesplit <line1>,<line2>s/,\? and \|, \|$/,\r/g

" This command expects one name per line. It finds all of the first
" and middle names, followed by the last name and a comma. It then
" rearranges the last name first, appends an and after each author,
" and lastly (piped) deletes the last trailing 'and'
command! -range=% Lnfn <line1>,<line2>s/\(\%([^, ]\+ \)\+\)\([^, ]\+\),\|$/\2, \1and/e | <line1>,<line2>s/\_.*\zsand//g

This is kind of messy, but it's challenging whenever you're dealing with names in LaTeX, which may contain things like \'{e}, so the name regexp has to be very general. I tried to pipe these through together, but it didn't work when the names got split into multiple lines.

  • 1
    DOUBTS : [[D1]] Does your 2 Command Method work on the very last line , which will not have the following "and" ? [[D2]] Can you put these two into a function & call that function with 1 Command ?
    – Prem
    Commented Oct 7, 2022 at 8:26
  • 1
    I don't think this is what you're looking for, but if you add -bar to your first definition (so it starts command! -bar -range=% Namesplit...) then you can chain your two commands with a pipe character: :Namesplit | Lnfn
    – Rich
    Commented Oct 7, 2022 at 9:04

2 Answers 2


The following command works on a single line. The command itself would need some refinement but it works. You may adapt it into a range function or a range command.

s/,\? and \|, \|$/,/g | s/\(.\{-}\) \(\S\+\(,\|$\)\)/\2 \1 and/g | s/ and$// | s/ and/ and\r/g

And a multiline command:

'<,'>s/\n/,/g | s/,\? and \|, \|,$/,/g | s/\(.\{-}\) \(\S\+\(,\|$\)\)/\2 \1 and/g | s/ and$// | s/ and/ and\r/g
  • Your multiple line version only provides a range to the first command in the sequence.
    – D. Ben Knoble
    Commented Oct 7, 2022 at 11:49
  • Yes because it joins all lines and then, the other commands operate on one line ; the last one adds carriage return.
    – jhoran
    Commented Oct 7, 2022 at 12:40
  • This works in most cases. What it won't work for are names like de la Fuente, but determining the last name in any case is not well-suited for a regular expression. To clarify a bit: The first pipe is the same as what I asked in the question and ensures each name ends with a comma. The second /\(.\{-}\) \(\S\+\(,\|$\)\)/ matches as few characters as possible before a space (to get the last name), then matches the rest of the characters (until a comma or end of line). The third just deletes the extra and at the last author, and the fourth adds newlines.
    – sdpoll
    Commented Oct 10, 2022 at 18:44

I would not use vim for this. I would use an awk script.

Here's the first iteration as a one-liner:

:'<,'>!awk 'BEGIN {RS=","} { f=split($0, name_arr, " "); print name_arr[f] ", " name_arr[1] " and"}'

But if you want to do this right (trim the word and, support first names that're more than 1 word long, not print out the trailing and) then you should write an awk script, save it in a file, and invoke it like so:

:'<,'>!awk -f names.awk

Here's my attempt at writing your awk script for you.

BEGIN {RS=","}
function format_name(str) {
  number_of_fields = split($0, name_arr, " ");
  first_name = ""
  for (i = 0; i < number_of_fields; i++) {first_name += name_arr[i]}
  last_name = name_arr[number_of_fields]
/and/ {
  print format_name(sub(/and /, ""))
{ print format_name($0) " and" }

Test it, fix it if it doesn't work, and then edit this answer (or comment on it and I'll edit it if you don't have enough reputation)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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