I find myself often in the situation in which I want to run multiple vim commands on a multiple files.

Consider file1.txt, file2.txt, file3.txt I would need to run :retab and then some :%s/ /, / for instance.

How can I pipe this through vim?

3 Answers 3


First, you are running Vim, not Vi, because the latter does not offer the retab command.

Assuming the file should be modified (that is what x does below), you can pipe the commands to Ex improved mode this way:

printf '%s\n' 'retab' '%s/ /, /' 'x' | vim -E file1.txt

Now, bear in mind that Ex will skip your .vimrc, so retab will not take tabstop and expandtab values from that file. Of course, you can just manually supply them, for example,

printf '%s\n' 'set expandtab' 'retab' '%s/ /, /' 'x' | vim -E file1.txt

To operate on various files (matching file[digit].txt), just wrap it in a shell loop,

for file in file[0-9].txt; do
    printf '%s\n' 'set expandtab' 'retab' '%s/ /, /' 'x' | vim -E "$file"
  • Could one also create a macro instead and use this with vim for each file?
    – stephanmg
    Sep 18, 2020 at 7:23
  • 1
    @stephanmg I think so, except maybe for the x. Do you want that case covered too? If so, please edit the question.
    – Quasímodo
    Sep 18, 2020 at 11:15
  • No, this will be fine @Quasimodo.
    – stephanmg
    Sep 18, 2020 at 13:43

You can use the -c argument in the command-line to run commands in Vim after opening a file. You can pass it multiple times to run the several separate commands. Once you run the editing commands, you'll probably want to include a :wq to have Vim save your changes and quit.

$ vim -c 'retab' -c '%s/ /, /' -c 'wq' file1.txt

You can of course use that inside a for loop to run it on each of your text files.

Another option is to open all the files in Vim and then use :argdo to run a sequence of commands in all the files in the argument list. In that case, you should join all your commands using | (which is the command separator in Vim) and you should end each :argdo with an :update so that it will save the file (if necessary) before proceeding to the next one. This is important, otherwise Vim might refuse to move away from the current buffer, which would break your :argdo execution.

$ vim file*.txt

Then inside Vim:

:argdo retab | %s/ /, / | update

All your files will have been updated and you'll be in the last file in your argument list. At this point you can simply quit Vim with :q.

  • 1
    I think the argdo version will work even as a -c (though you may need to watch out for the default value of 'hidden')
    – D. Ben Knoble
    Sep 17, 2020 at 19:59
  • @Ben with | update at the end, 'hidden' doesn't matter though... right? You can do vim -c 'argdo retab | %s/ /, / | update' -c 'q' file*.txt, that should work.
    – filbranden
    Sep 17, 2020 at 20:09
  • 2
    should work :) I seem to have hazy memories of something going wrong like that, though I could have been forgetting | update
    – D. Ben Knoble
    Sep 17, 2020 at 21:05

With the following code, open all the files in vim, and then execute :Bufdo retab

function! Bufdo(command)
  let curbuf=bufnr("%")
  execute 'bufdo ' . a:command
  execute 'buffer ' . curbuf
command! -nargs=+ -complete=command Bufdo call Bufdo(<q-args>)

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.