How can I edit all the files returned by find in vi in Linux?

27

7

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?

abeger

Posted 2011-09-15T15:46:29.017

Reputation: 725

Related: Terminal borked after invoking Vim with xargs.

– kenorb – 2017-11-16T11:49:47.703

Piping to xargs destroyed my shell terminal. The proper solution is the one below by @DevSolar – typelogic – 2018-09-13T14:36:02.430

1You can pipe it into vi: find . "*.txt" | xargs vi – MaQleod – 2011-09-15T15:55:12.177

1@MaQleod: Technically it would be piping to xargs. – user1686 – 2011-09-15T16:18:47.947

Answers

41

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.

DevSolar

Posted 2011-09-15T15:46:29.017

Reputation: 3 860

No need to escape the *, globbing won't happen inside the double quotes. – Kusalananda – 2011-09-15T15:57:30.683

Just realized that myself when putting it to the test... thanks. – DevSolar – 2011-09-15T15:59:26.123

4Don'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 – 2011-09-15T16:17:15.213

1@DevSolar, your solution was better than mine (now deleted) which tried to execute vi through xargs, which doesn't work since Vi won't get a terminal for input/output properly. – Kusalananda – 2011-09-15T16:17:43.123

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 – 2011-09-15T16:18:20.943

@grawity: That explains a lot. Thank you very much indeed! You fully deserve a "correct answer" for that, so I made it a proper question.

– DevSolar – 2011-09-15T16:27:01.303

3@zpletan: vim -p if you want file tabs. (gt and gT to navigate, or click with your mouse.) – user1686 – 2011-09-15T16:45:33.037

2FYI, to avoid the warning when piping into vim, just specify - as the first argument, as in echo foobar | vim -. – Konrad Rudolph – 2011-09-15T18:47:17.917

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 – 2011-09-16T07:06:14.680

@DevSolar Well, you still need to use xargs (of course). – Konrad Rudolph – 2011-09-16T07:17:43.143

@Konrad Rudolph: "Too many editor arguments". Sorry, it just doesn't work. Have you tried it yourself? – DevSolar – 2011-09-16T07:41:02.797

@DevSolar Ah, I see the problem: vim - tells vim to get the input from STDIN instead of a file. Conversely, with find you want to pass multiple arguments to vim – quite the opposite. Sorry. – Konrad Rudolph – 2011-09-16T09:46:50.143

@Konrad Rudolph: Yeah, it wasn't what I was looking for in this post, but piping command output into a vi buffer is also something I was wondering about. Thanks! – abeger – 2011-09-16T16:29:51.963

4

Or run vim and from there:

:args **/*.txt

Benoit

Posted 2011-09-15T15:46:29.017

Reputation: 6 563

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.

kenorb

Posted 2011-09-15T15:46:29.017

Reputation: 16 795

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

OldWolf

Posted 2011-09-15T15:46:29.017

Reputation: 2 293

...but why would you want to do that? – DevSolar – 2011-09-15T16:29:58.333

@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 – 2011-09-15T18:12:23.830

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 – 2011-09-16T08:38:19.827

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 – 2011-09-16T11:34:03.727

@ceving A valid point. I presumed the original posters find was valid for his needs. Editing. – OldWolf – 2011-09-16T14:01:39.297

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.

Cory Klein

Posted 2011-09-15T15:46:29.017

Reputation: 1 232

thank you I connected it to locate so my command was: locate filename | xargs vim -p – talsibony – 2016-09-13T08:44:09.803