6

I usually use like this

$ find -name testname.c
./dir1/dir2/testname.c
$ vi ./dir1/dir2/testname.c

it's to annoying to type file name with location again.

how can I do this with only one step?

I've tried

$ find -name testname.c | xargs vi 

but I failed.

Sungguk Lim
  • 6,109
  • 7
  • 43
  • 63
  • Possible duplication: http://stackoverflow.com/questions/5955577/bash-automatically-capture-output-of-last-executed-command-into-a-variable – K Z Apr 06 '12 at 03:05
  • This answer explains why the `xargs` solution doesn't work: [Why does “locate filename | xargs vim” cause strange terminal behaviour?](http://stackoverflow.com/a/8228888/85371) – sehe Apr 06 '12 at 10:14

5 Answers5

9

Use the -exec parameter to find.

$ find -name testname.c -exec  vi {} \;

If your find returns multiple matches though, the files will be opened sequentially. That is, when you close one, it will open the next. You won't get them all queued up in buffers.

To get them all open in buffers, use:

$ vi $(find -name testname.c)

Is this really vi, by the way, and not Vim, to which vi is often aliased nowadays?

Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • Adding a + instead \; to find would be better for opening multiple files. + syntax is defined by POSIX and should be available. – jordanm Apr 06 '12 at 04:56
6

You can do it with the following commands in bash:

Either use

vi `find -name testname.c` 

Or use

vi $(!!)

if you have already typed find -name testname.c

Edit: possible duplication: bash - automatically capture output of last executed command into a variable

Community
  • 1
  • 1
K Z
  • 29,661
  • 8
  • 73
  • 78
4

The problem is xargs takes over all of vi's input there (and, having no other recourse, then passes on /dev/null to vi because the alternative is passing the rest of the file list), leaving no way for you to interact with it. You probably want to use a subcommand instead:

$ vi $(find -name testname.c)

Sadly there's no simple fc or r invocation that can do this for you easily after you've run the initial find, although it's easy enough to add the characters to both ends of the command after the fact.

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • can I ask you something?. you mean that `xargs` pass not only the `./dir1/dir2/testname.c` in this example? – Sungguk Lim Apr 08 '12 at 08:07
  • File descriptors are inherited; normally `vi` inherits its input from the shell, whose input is your terminal, but when you use `xargs` at the end of a pipeline, the inherited input would be the pipeline. `xargs` does not want to give the pipeline to the program it's running (since at best it is passing a consumed pipeline and at worst the program would consume input intended for `xargs`) so it substitutes `/dev/null`. – geekosaur Apr 08 '12 at 08:14
3

My favorite solution is to use vim itself:

:args `find -name testname.c`

Incidentally, VIM has extended shell globbing builtin, so you can just say

:args **/testname.c

which will find recursively in the sub directory tree.

Not also, that VIM has filename completion on the commandline, so if you know you are really looking for a single file, try

:e **/test

and then press Tab (repeatedly) to cycle between any matchin filenames in the subdirectory tree.

sehe
  • 374,641
  • 47
  • 450
  • 633
1

For something a bit more robust than vi $(find -name testname.c) and the like, the following will protect against file names with whitespace and other interpreted shell characters (if you have newlines embedded in your file names, god help you). Inject this function into your shell environment:

# Find a file (or files) by name and open with vi.
function findvi()
{
    declare -a fnames=()
    readarray -t fnames < <(find . -name "$1" -print)
    if [ "${#fnames[@]}" -gt 0 ]; then
        vi "${fnames[@]}"
    fi
}

Then use like

$ findvi Classname.java
Reed Sandberg
  • 671
  • 1
  • 10
  • 18