Basic example using grep (note that grep would not be my only use-case for this):

$ grep -Irl "foo"

Now I want to open the config.js file in Vi. My normal method would be:

$ vi path/to/directory/config.js

which I had to either type by hand with the assistance of tab-completion, or I highlighted the filename from the grep result and copy/pasted it.

But I'd like to be able to Vi the file by just specifying that it was the 2nd result in the grep command. So something like:

$ grep -Irl "foo" | xargs vi 2

Obviously xargs would not work like that, was just an example. But I'm trying to find if there's a way using xargs (or any other utility) to accomplish this and I'm not finding it.

Something that a teammate suggested to me was to use head and tail together, like this:

$ grep -Irl "foo" | tail -n 1 | xargs vi

would retrieve task.js, and

$ grep -Irl "foo" | head -n 2 | tail -n 1 | xargs vi

would retrieve config.js. Wondering if there is a less verbose method.

  • 1
    Selecting by line number seems like a strange thing to do. How do you know the line number if you haven't first displayed the output on the terminal? And then you have to count lines to figure out which you want. Why not grep -Irl "foo" path | grep config?
    – Barmar
    Nov 3 '17 at 19:00
  • It's unlikely that I know that config.js exists before doing this, so first I'd just perform the grep. Then when I see a file I want to do something to, I'd hit the up-arrow to run the grep again and pipe the result to something that would open the file I want. The goal is to avoid using my mouse to copy/paste a file path. A co-worker showed me I can also use screen to select text anywhere on the screen using a keyboard. I realize this might not save a lot of time, especially if the lines are too many to count and now I'm having to display line numbers in my output. Nov 3 '17 at 22:38

How about

   vi $( grep -IrL "foo" | awk "NR==3" )

This uses AWK to find the appropriate line. The output is then used as a command line argument for vi.

I'm oldschool, so I prefer the equivalent, but slightly frowned upon version-

 vi ` grep -IrL "foo" | awk "NR==3" `

Not quite what was asked for because it doesn't use a pipeline into vi, but using sed:

vi $( grep -Irl "foo" | sed -n '2 p')

  • 1
    Nothing to stop this being done by piping into vi - grep -Irl "foo" | sed -n '2 p' | xargs vi
    – davidgo
    Nov 2 '17 at 22:06
  • Now I have to vote between using sed vs. awk. Seems like awk is generally favored as it can do everything sed can do plus more? I also prefer the awk method syntactically. stackoverflow.com/questions/1632113/… Nov 2 '17 at 23:07

I have written a wrapper function for this purpose, which you can use if you want to reuse an output line from a command.

First the function for bash:

catch() {
   nl <<< "$out"
   read -a r -d '\n' <<< "$out"
   r=("dummy" "${r[@]}")

Then for zsh:

catch () {
   nl <<< "$out"

The usage is for both variants the same. First, do a run of the desired command, prepended with catch, which will present you the command's output with a nice numbering:

$ catch grep -Irl "foo"
   1  path/to/dir/task.js
   2  path/to/dir/help.js
   3  path/to/dir/config.js

Now, you can reuse any line in a following command by using the array parameter $r, e.g.

$ wc -c ${r[2]}
14044 path/to/dir/help.js

In zsh you can omit the curly brackets (wc -c $r[2]) but in bash you need them unfortunately.

Some caveats which come to my mind:

  • the output is only displayed after the command has finished
  • strange charactes in output might produce erratic behaviour (e.g. newlines in filename)
  • only output to STDOUT is captured (if some output is written to STDERR, it'll get unmixed on the console and ignored in $r)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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