7

I have a directory structure based on events.

In these events, I have one type of file which does or does not contain a pattern for which I need to search. The positive results I would like to store in a separate file.

My first approach was:

find . /EVENT*/'filename' | grep 'searchtext' head -2 > error_file

but that does not seem to work. I was told that it is not possible to combine find and grep in this way, so how do I need to do it?

  • Note that what you are doing here is (approximately; this is not technically 100% correct, but it's close enough for a first order approximation to give you an idea what's actually going on) first run find . /EVENT*/'filename', then take whatever that command prints on its standard output (which in the case of find by default will be a list of file names) and pass that as standard input into the command grep 'searchtext' head -2 (which doesn't make a lot of sense as grep won't look at stdin when given file names), then redirect standard output of grep into the file error_file. – a CVn Mar 15 '15 at 21:48
  • If I interpret it correctly, it looks like there is a glob-like expansion in /EVENT*/'filename'. I'm not sure how to do this with find, and AFAIK bash cannot do this. All the answers so far have missed this. – Sparhawk Mar 15 '15 at 22:26
14

Here is a general pattern:

find /directory/containing/files -type f -exec grep -H 'pattern_to_search' {} +

Here at first find will search all files in the directory containing necessary files, you can also use wildcards e.g. *.txt to look for only files ending with .txt. In that case the command would be:

find /directory/containing/files -type f -name "*.txt" -exec grep -H 'pattern_to_search' {} +

After finding the necessary files we are searching for the desired pattern in those files using -exec grep -H 'pattern_to_search' {} + (-H will print the filename where the pattern is found). Here you can think of {} as containing the files to be searched and + is needed to be used at the end with -exec so that -exec will be forked only once i.e. grep will search as if the command is grep -H 'pattern_to_search' file_1.txt file_2.txt file_3.txt.

  • Okay -- I guess this is the reason: superuser.com/questions/140689/… If there ends up being only one file in the match, grep won't print the file name, so adding /dev/null ensures there's at least two if there's a match. But using -H seems a more normative, less zany-hack style method (and less typing). I'd change it to that ;) – goldilocks Mar 15 '15 at 18:46
  • I also think you need '{}', not plain {}. Otherwise I get missing argument to -exec. – goldilocks Mar 15 '15 at 18:50
  • @goldilocks - you shouldn't have to quote that. – mikeserv Mar 15 '15 at 18:52
  • @mikeserv Hmmmph, looks like it is a fish shell issue -- exact same thing under bash is fine. I remember getting confused about this a while back, I guess it was when I starting using fish as my default. – goldilocks Mar 15 '15 at 19:04
  • I'd also go for single quotes rather than doubles, ski as to avoid any unexpected interpolations – roaima Mar 15 '15 at 19:51
5

Another valid method is:

 find /directory/containing/files -type f -print0 | xargs -0 grep "test to search"

The print0 / option -0 is to allow for the case where filename strings contain special characters (like spaces).

  • Using -exec grep "test to search" {} + handles filenames with special characters just fine. The only reason not to use it is that some systems do not support it. – AKHolland Mar 17 '15 at 18:27
3

And one more way, in just a single process (without find, -exec, xargs):

grep -r "test to search" /directory/containing/files

Well, with GNU grep, anyway. :)

ETA: Since I've been asked to show the grep --include option, here's an analogue to heemayl's example ("you can also use wildcards e.g. *.txt to look for only files ending with .txt"):

grep --include '*.txt*' -r "pattern to search" /directory/containing/files

And while I'm editing, yes, these are alternatives, not drop-in replacements: These commands will read (regular) files through symlinks. (They won't recurse through symlinked directories though; for that you need -R.) They will also read sockets, named pipes, and device files (though -D skip can be added to, um, skip those). And there's no way with grep to get the effect of find options like -mtime -2. If you need those – use find. If you don't, well, I'd argue grep is a nice alternative.

  • 2
    This is different in that grep then searches all files (which is sometimes not fun) rather than those matching a find pattern. – goldilocks Mar 15 '15 at 19:08
  • 3
    @goldilocks Sure, if you have a find pattern. Except, you can give patterns to grep -r as well; most commonly --exclude=GLOB (or --include=GLOB) and --exclude-dir=GLOB, though I guess you sometimes need -D skip and/or -I. But sure, if you have -mtime -2 or somesuch, you need that find. grep -r is another option, not a replacement. – The Sidhekin Mar 15 '15 at 19:22
  • @The Sidhekin: you should edit your post to add the --include option. This is the correct answer. – jopasserat Mar 16 '15 at 11:31
  • @jopasserat Really? What to --include though? The original does-not-work approach didn't specify any. Though I guess I could add an analogue to heemayl's can-also-use example … – The Sidhekin Mar 16 '15 at 11:47
  • Well considering OP's question, he seems to have a particular type of files to search in. So I'd say --include='pattern' with pattern matching whatever he wants. – jopasserat Mar 16 '15 at 12:39
0

I will assume that you need to grep for a particular pattern through text files and recurse through subdirectories. If you are using GNU grep, it is pretty simple:

cd /to/the/top/level    
grep -r "PatternToSearch" *

Instead of -r, you can use -R or --recursive

You must use quotes if you have spaces in your search pattern.

There are a lot of options to GNU grep. Just type in grep --help and you will be amazed !

  • How do you ask grep to look at only files that match a particular patter? Say, filename extension? This proposal will make grep look in every file in the file system under this folder: that's not what the question is trying to do. – GreenAsJade Aug 4 '16 at 9:20
  • Something like *.log should do. By the way, the Silver Searcher would be more handy and faster for this: ag SearchWord FilePattern – Hopping Bunny Feb 20 '18 at 11:34

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.