5

I often want to run multiple vim commands on multiple files.

Consider the files file1.txt, file2.txt, and file3.txt: I might want to run :retab and then some :%s/ /, / for instance.

How can I "pipe" this through Vim?

4 Answers 4

6

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"
done
3
  • 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
5

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.

3
  • 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
1

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
endfunction
command! -nargs=+ -complete=command Bufdo call Bufdo(<q-args>)
1

A trivial way without complex commands is to use recording and replays (help q and help @).

Specifically you'll

  1. open all the files at once
$ vim file1.txt file2.txt file3.txt
# (Now you should be seeing contents of `file1.txt`)
  1. Press qq (actually any letter may come after q since it refers to a register, but qq is fastest to type), you'll see recording @q in the status line. Any subsequent operations will be recorded to @q.
  2. Perform your changes to file1.txt e.g. :retaband :%s/ /,/ . The recording mechanism will save these operations to the register q so you may replay it in the future.
  3. Save the changes to file1.txt, and goto the next file with :wne
  4. Finish recording by pressing q. Now in q contains commands to execute your modifications and go to the next file.
  5. Replay those commands by pressing @q. You should see the modifications being made to file2.txt and finally going to file3.txt
  6. Repeat @q until you see E165: Cannot go beyond last file, that's when the job's done.

Your Answer

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

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