74

Is there a way to execute a Vim command on a file from the command line?

I know the opposite is true like this:

:!python %

But what if I wanted to :retab a file without opening it in Vim? For example:

> vim myfile.c
:retab | wq

This will open myfile.c, replace the tabs with spaces, and then save and close. I'd like to chain this sequence together to a single command somehow.

It would be something like this:

> vim myfile.c retab | wq
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
syvex
  • 7,518
  • 9
  • 43
  • 47

3 Answers3

66

This works:

gvim -c "set et|retab|wq" foo.txt

set et (= set expandtab) ensures the tab characters get replaced with the correct number of spaces (otherwise, retab won't work).

I don't normally use it, but vim -c ... also works

The solution as given above presumes the default tab stop of eight is appropriate. If, say, a tab stop of four is intended, use the command sequence "set ts=4|set et|retab|wq".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Firstrock
  • 931
  • 8
  • 5
  • I tried to run this on multiple files using `xargs` (`find -name \*.[ch] | xargs -n 1 vim -c "set et|retab|wq"`), but that was really slow. On each invocation, vim would complain that input was not from a terminal and I think it delays for a second or two to allow reading the message. Alternatively, a for loop leaves stdin untouched and so is fast: `for f in $(find -name \*.[ch]); do vim -c "set et|retab|wq" $f; done`. This is not perfect, though, e.g. filenames with spaces or too many filenames break this (so maybe vim has an option to suppress that warning). – Matthijs Kooijman Apr 16 '20 at 08:35
58

You have several options:

  • -c "commands" : will play Ex commands as you entered them in the command line.
    In your example : vim myfile -c 'retab | wq'. This is what Firstrock suggested.

  • -S "vim source file" : will source given vim script
    (like running vim -c "source 'vim source file'"):

    If you have a file script.vim containing:

    retab
    wq
    

    Then you can use vim myfile.c -s script.vim (the extension does not really matter)

  • -s "scriptin file": will play contents of file as it contains normal mode commands: If you have script.txt containing:

    :retab
    ZZ
    

    with end of lines consisting of a single ^M character (for example you saved the script using the :set fileformat=mac | w), then you can run: vim myfile.c -S script.txt (ZZ is another way to exit vim and save current file).
    Note that you can record those scripts with vim my_file -W script.txt, but it suffers a bug if you happen to use gvim (the GUI).

Community
  • 1
  • 1
Benoit
  • 76,634
  • 23
  • 210
  • 236
  • 1
    If you, like me, just want to execute a handful of commands, in some shells you can use `vim -S <(echo -e "VundleInstall \n q \n q")`. – Minix Dec 13 '16 at 10:49
2

Not a direct answer to your question, but if you want to replace tabs with spaces (or do any other regex search/replace) for a list of files, you can just use in-place sed search/replace:

sed -i 's/\t/   /g' foo1.txt foo2.txt

or

ls *.txt | xargs sed -i 's/\t/   /g'

(In this example I am replacing each tab character with three spaces.)


NOTE: the -i flag means operate in-place.

From the sed man page:

   -i[SUFFIX], --in-place[=SUFFIX]

          edit files in place (makes backup if  extension
          supplied)
arr_sea
  • 841
  • 10
  • 16
  • You're right - one shouldn't use the microscope like a can opener – oxfn Mar 04 '16 at 23:40
  • 1
    In my experience if the file is huge (order of 100s of MB) and the number of replacements are limited, then sed is painfully slow. The 'vim -s ' option suggested by @benoit works much faster - order of seconds. In my case, sed took about 3 minutes, while the vim script option completed in about 25 sec for a file 200 MB large and about 100 replacements – Prashant Nov 28 '17 at 06:16