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?
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.
Sign up to join this communityFirst, 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
vim
for each file?
Sep 18, 2020 at 7:23
x
. Do you want that case covered too? If so, please edit the question.
Sep 18, 2020 at 11:15
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
.
argdo
version will work even as a -c
(though you may need to watch out for the default value of 'hidden')
| 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.
| update
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>)
A trivial way without complex commands is to use recording and replays (help q
and help @
).
Specifically you'll
$ vim file1.txt file2.txt file3.txt
# (Now you should be seeing contents of `file1.txt`)
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
.file1.txt
e.g. :retab
and :%s/ /,/
. The recording mechanism will save these operations to the register q
so you may replay it in the future.file1.txt
, and goto the next file with :wne
q
. Now in q
contains commands to execute your modifications and go to the next file.@q
. You should see the modifications being made to file2.txt
and finally going to file3.txt
@q
until you see E165: Cannot go beyond last file
, that's when the job's done.