With GNU tools (your --color=always
is a GNU extension already) and a shell with support for Ksh-style process substitution:
xargs -r0a <(grep -rlZ pattern .) vim
Or:
xargs -r0a <(find . ... -exec grep -lZ pattern {} +) vim
With zsh
:
vim ${(0)"$(find . ... -exec grep -lZ pattern {} +)"}
With bash
4.4+:
readarray -td '' files < <(find . ... -exec grep -lZ pattern {} +)
vim "${files[@]}"
Those minimise the number of grep
invocations that are performed. The point is to tell grep
to output the file names NUL-delimited, so they can be reliably split into separate arguments for vim
by GNU xargs -0
or zsh
's 0
parameter expansion flag or bash
's readarray -td ''
.
In zsh
, you could also do:
vim ./**/*(...e['grep -q pattern $REPLY'])
(where ...
stands in for further qualifiers you may want to add, like for the find
approach).
That means however that like the approaches that use find -exec grep -q pattern {} ';'
, one grep
invocation would be run per file which would make it significantly slower.
Your first approach would work in zsh
provided you replaced --color=always
with -Z
and changed the value of IFS
to IFS=$'\0'
from the default of IFS=$' \r\n\0'
. I wouldn't work in other shells, as they don't support storing NULs in variables, let alone $IFS
and would also perform filename generation on the words resulting of the splitting which you'd need to disable with set -o noglob
or set -f
.
Why is looping over find's output bad practice? will give you more tips on how to process the list of files found by find
reliably.
--color=always
? With-l
grep only lists matched files anyway, so you really shouldn't need colors. – Quasímodo Sep 07 '20 at 15:26-l
and removing--color=always
takes more or less the same fraction of time. And changing the latter to the former takes even less time. I've modified the question to get some more knowledge about the command substitution approach vs thexargs
approach. – Enlico Sep 07 '20 at 15:37