29

Something I find myself doing a lot is running a find command and then editing all of them in vi, which looks something like this:

> find . "*.txt"
./file1.txt
./file2.txt
./path/to/file3.txt

> vi ./file1.txt ./file2.txt ./path/to/file3.txt

Is there a clever & simple way to do this all in one command line?

4
  • 1
    You can pipe it into vi: find . "*.txt" | xargs vi – MaQleod Sep 15 '11 at 15:55
  • 1
    @MaQleod: Technically it would be piping to xargs. – user1686 Sep 15 '11 at 16:18
  • Related: Terminal borked after invoking Vim with xargs. – kenorb Nov 16 '17 at 11:49
  • Piping to xargs destroyed my shell terminal. The proper solution is the one below by @DevSolar – eigenfield Sep 13 '18 at 14:36
44

This should do the trick:

find . -name "*.txt" -exec vim {} + 

Use Vim, it's better for your health. :-)

The oft-overlooked + option to -exec makes all filenames (up to line length limits) appear in one line, i.e. you still get all the files opened in one vim session (navigated with :n for next and :N for previous file).

With vim -p you get a file tab for each file. Check :help tab-page-commands for more details.

With vim -o you will get horizontally split windows for each file, vim -O vertically split windows. Check :help window-move-cursor for more details.

Note that the previous version of this answer, vim $(find . -name "*.txt"), does not work with spaces in filenames, and has security implications.

Piping into xargs vi gives a Warning: Input is not from a terminal, plus a terminal with completely bogus behaviour afterwards. User grawity explained why in a comment below, and with a bit more explanation in this question.

13
  • 4
    Don't know how much you know about vim, but it took me /forever/ to figure out that :n shows the next file and :N shows the previous one. – zpletan Sep 15 '11 at 16:17
  • 4
    @DevSolar: Vim expects its stdin to be the same as its controlling terminal, and performs various terminal-related ioctls on stdin directly. (You could consider this a bug. Vim certainly can open /dev/tty and call ioctl() on that; it's just too lazy to do it.) When invoked by xargs, vim receives /dev/null as its standard input, which just ignores terminal-control ioctls. – user1686 Sep 15 '11 at 16:18
  • 3
    @zpletan: vim -p if you want file tabs. (gt and gT to navigate, or click with your mouse.) – user1686 Sep 15 '11 at 16:45
  • 2
    FYI, to avoid the warning when piping into vim, just specify - as the first argument, as in echo foobar | vim -. – Konrad Rudolph Sep 15 '11 at 18:47
  • 2
    @Konrad Rudolph: find . -name "*.txt" | vim - gives you a vim session on an unnamed file containing the names of the found files, which is not what the OP asked for... – DevSolar Sep 16 '11 at 7:06
4

Or run vim and from there:

:args **/*.txt
1

Additionally, if you wanted them opened one at a time, you can also use find -exec or use a simple for loop. Edited per ceving's comment.

find . -name "*.txt" -exec vi {} \;

or

OLDIFS=$IFS
IFS=$(echo -en "\n\b")
for i in `find . -name "*.txt"`
    do
        vi $i
    done
IFS=$OLDIFS
5
  • ...but why would you want to do that? – DevSolar Sep 15 '11 at 16:29
  • @DevSolar The first is to point out the capability in find, the second is a general purpose loop. Maybe you want to do something to every file before you edit it. – OldWolf Sep 15 '11 at 18:12
  • Have you ever tried the second? First: the find returns the current directory because -name is missing. And second: the command fails miserably as soon as a file name contains a space. -1 for ill-advised answer. – ceving Sep 16 '11 at 8:38
  • Actually, the subject of spaces in filenames applies to my answer just as well. I cannot even think of a way to handle them properly without turning it from a command line into a little script of its own. There is a good reason why spaces in filenames are discouraged. – DevSolar Sep 16 '11 at 11:34
  • @ceving A valid point. I presumed the original posters find was valid for his needs. Editing. – OldWolf Sep 16 '11 at 14:01
1

To edit all *.txt, you can just run: vim *.txt. To edit files returned by find, read futher.


On Unix/macOS, you can use find with BSD xargs (see: man xargs), e.g.

find -L . -name "*.txt" -type f -print0 | xargs -o -0 vim

-o (xargs): Reopen stdin as /dev/tty in the child process before executing the command.

Related: Terminal borked after invoking Vim with xargs at Vim.SE.

0

If you've already typed out your find command I find that it can be much easier to use xargs to open the search results:

find . -name "*.txt" | xargs vim -p

The -p option tells vim to open each file in a new tab. I find this more convenient than just using buffers, but if you don't then just leave off that option.

1
  • thank you I connected it to locate so my command was: locate filename | xargs vim -p – talsibony Sep 13 '16 at 8:44

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