10

Suppose that I have a file named filename123.txt and it is the single file that is named so, and I can locate it with the command locate filename123. And it returns only this file.

Now I want to open it with vi/vim. But I don't want to go to that location and type the vi command followed by filename. Here I want the result of locate filename123 to be appended to the vi command. How can I do so? I already tried:

locate filename123 | vi

But this does not works. And this error comes in terminal:

santosh@santosh:~$ locate filename123 | vi
Vim: Warning: Input is not from a terminal
Vim: Error reading input, exiting...
Vim: Finished.
kenorb
  • 20,988

3 Answers3

12

You can use xargs:

locate filename123 | xargs vi

By default xargs will execute as few instances of the specified command as possible, passing as many parameters as possible according to the system's ARG_MAX. To limit the number of parameters passed to an instance of vi, use xargs' -n option.

To handle file names containing spaces use xargs' -d option:

locate filename123 | xargs -d '\n' vi

To handle file names containing newlines use xargs' -0 option together with locate's -0 option:

locate -0 filename123 | xargs -0 vi

(If -0 is not available on any of them, check for --null too, or another way to specify character \000 as delimiter.)

manatwork
  • 31,277
3

You could use command expansion:

vi "$(locate filename123 | head -n1)"

To make vim read its stdin you can do vim -, but that will just give you the output of locate which are filename paths. This might work though, using vim gf normal command will then open the path that the cursor is on.

Edit

In the case of filenames with spaces in them you're better of using the xargs option mentioned or the vim solution described above. Note to make gf work you need to add space to isfname:

set isfname+=32

This tip and alternatives are described here.

Edit 2

Added quotes and head as suggested by PeterO in the comments.

Thor
  • 17,182
  • For file with whitespaces in them, when I insert \ in file name, like vi $(locate another\ file). The vim opens it as two separate files, another and file :(. – Santosh Kumar Aug 18 '12 at 12:20
  • Your answer only works when I have no spaces in the file which I am locating. – Santosh Kumar Aug 18 '12 at 12:25
  • Answer updated. – Thor Aug 18 '12 at 12:45
  • 2
    @Thor: For a single file, as mentioned in the question, you just need to wrap quotes around the process-substitution: vi "$(locate filename123)" ... To ensure that it has only one file name to deal with: vi "$(locate filename123 |head -n1)" – Peter.O Aug 18 '12 at 17:49
  • PeterO: You're right, I only tested with multiple hits. I've added your comments to the answer. – Thor Aug 18 '12 at 18:29
1

The simplest way is to pass locate as shell substitution, like:

vim $(locate filename123)

You can also consider to use find instead of locate to pass file names to edit, in example:

find . -name 'filename123' -exec vim {} +

On Unix/OSX you can use xargs with -o parameter, like:

locate filename123 | xargs -o vim

-o Reopen stdin as /dev/tty in the child process before executing the command. This is useful if you want xargs to run an interactive application.

Or on Linux try the following workaround using:

locate filename123 | xargs -J% sh -c 'vim < /dev/tty $@'

If you're using different commands, use command substitution to achieve that, like:

vim $(locate filename123)
vim `locate filename123`

Alternatively use GNU parallel instead of xargs to force tty allocation, in example:

locate filename123 | parallel -X --tty vi

Note: parallelon Unix/OSX won't work as it has different parameters and it doesn't support tty.

Many other popular commands provides pseudo-tty allocation as well (like -t in ssh), so check for help.

Other suggestion would be to use vipe (a Vim command pipe) or use the following simple script:

#!/bin/sh
# usage: locate filename123 | vip
exec vim < /dev/tty "$@"

Related:

kenorb
  • 20,988