4

So I'm relatively new to command line. I was able to use find to get an output of multiple files from multiple directories since there was no specific place these would be (I'm sure this can be shortened):

find ./ -name filename1.ext && find ./ -name filename2.ext && find ./ -name filename3.ext

Now that gave me the list of what I was wanting but now that I've found the files in question, I want to grep them for information.

6

You can group all the name primaries in a single find statement then have find execute grep.

find . \( -name filename1.ext -o \
      -name filename2.ext -o \
      -name filename3.ext \) \
      -exec grep 'pattern' {} \;
  • Thank you so much for your help that makes a ton more sense to do this that way! – trazinaz Jun 27 '18 at 17:29
  • 3
    You might want to consider -exec grep 'pattern' {} + which bunches the filenames found and calls grep with multiple filenames, improving performance. That will affect the output (by default the lines matched are prefixed with the filename) -- if you don't want that you can add the -h option to grep. – glenn jackman Jun 27 '18 at 17:32
  • Yes that is way better for sorting the files the way I wanted the information. – trazinaz Jun 27 '18 at 17:56
  • @glennjackman "improving performance" Not necessarily. It would if there are many many files that are quickly matched so that calling grep for each one is somewhat significant. However, it might be more likely that ./ is a big tree and that there'll be long pauses between finding the different files. In that case, using \; instead of + would improve performance as you'd get partial results as each file is found. – JoL Jun 27 '18 at 18:35
  • Also look at options to grep... -i ignores the case of letters, -l just output the filename (of files with match), -H prefix matching lines with filename (to see what line(s) belongs to which file). Also look at fgrep and egrep. – Baard Kopperud Jun 27 '18 at 18:49
3

Try this,

find ./ -type f \( -name filename1.ext -o -name filename2.ext -o -name filename3.ext \) -exec grep 'string' {} \;
  • -type f since you’re looking for files, it's better to specify the type to get the result faster.

  • -o means OR, it enables you to add more filenames to the search

  • I appreciate that it helped me using this to make my combined command! – trazinaz Jun 27 '18 at 17:30
1

Beside the correct answer is indeed using -o and -exec, here's a general way to capture the output of a previous command and parse it line by line

(find .... && find ... && cat ... && ls ... && ...) | while read line; do grep string $line; done
0

Using bash:

shopt -s globstar
grep 'pattern' ./**/filename[123].ext

With the globstar shell option enabled, the ** pattern behaves like *, but matches across / in pathnames. This will work unless the pattern matches thousands of files, in which case you are likely to get an "Argument list too long" error from the shell. This also does not check whether the matched pathnames are for regular files or not, like a find would do with its -type f test. Also. if the pattern does not match anything, it will remain unexpanded.

In a loop, which solves all three of the above mentioned issues:

shopt -s globstar
for pathname in ./**/filename[123].ext; do
    [ -f "$pathname" ] && grep 'pattern' /dev/null "$pathname"
done
  • All the limitations you mention (and the fact that it skips hidden files) are easily addressed with zsh where that **/ syntax comes from. – Stéphane Chazelas Jun 27 '18 at 21:08
  • Also note that it would fail for files whose path starts with -, and the loop approach won't print file names (for the non-loop one, that will depend on whether one or more files are found) – Stéphane Chazelas Jun 27 '18 at 21:10
0

In general there are at least three ways to do a find + grep combination:

  1. grep pattern `find dir find-specifiers -print`
  2. find dir find-specifiers -exec grep pattern {} \;
  3. find dir find-specifiers -print | xargs grep pattern

And of course there's nothing special about grep here; these same three patterns could be used for find plus any command.

Number 1 is, in a sense, the oldest and most basic way, since backquotes have always been the way to capture the output of one command and use it on the command line of another. (These days, I get the impression that there's a newer bashism that's better than backquotes and that all the cool kids use, but I guess I'm an old-timer.) The disadvantage of number 1 is that if find finds lots of files, you may get the error "Command line too long".

Number 2 is a special feature built into find for doing a find + command combination. It's fine as far as it goes, but it has two disadvantages: (1) it re-invokes the auxiliary command (grep or whatever) for each file found, so it can be slow, and (2) if the auxiliary command is grep, since each invocation of grep sees one filename it won't list the filenames in the match, although you can work around that by doing -exec grep pattern {} /dev/null \; or, these days, -exec grep -H pattern {} \;.

And then there's number 3. As far as I know, xargs was invented to get around the limitations of the first two. Although xargs is theoretically a general-purpose program, I suspect in practice it's hardly ever used with any pair of programs other than find and grep. It thoroughly works around the disadvantage of #1; it'll work with arbitrary numbers of found files. It's efficient, although if you're unlucky it'll occasionally invoke grep one one last filename, meaning that you'll still want to use the /dev/null or -H trick. And it's got a disadvantage of its own: it doesn't work if any of the found filenames contain whitespace. But there's a way around that, too:

find dir find-specifiers -print0 | xargs -0 grep pattern

(I dearly wish xargs had been written to accept newline-separated filenames on its input by default instead of whitespace, but that's a rant for another day.)

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.