Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to find all files that are of a certain type and do not contain a certain string. I am trying to go about it by piping find to grep -v

example:

find -type f -name '*.java' | xargs grep -v "something something"

This does not seem to work. It seems to be just returning all the files that the find command found. If it helps, what I am trying to do is basically find all .java files that match a certain filename(e.g. ends with 'Pb' as in SessionPb.java) and that do not have an 'extends SomethingSomething" inside it.

My suspicion being a newbie to unix is that I'm doing it wrong. So how should the command look like instead?

share|improve this question
    
you might add what makes you think it didn't work. maybe your grep expression is too explicit? needs a '-i' for case insensitivty? See my answer below too... –  lornix Jul 5 '12 at 19:09

3 Answers 3

up vote 10 down vote accepted

There is no need in xargs here. Also you need to use grep with -L option (files without match), cause otherwise it will output the file content instead of its name, like in your example.

find . -type f -iname "*.java" -exec grep -L "something somethin" {} \+
share|improve this answer
    
This works but not with the negation part(e.g. when i add -v or -vi). –  Hyangelo Jul 5 '12 at 19:31
1  
-L already has negotiation, because it means files _without_ match. Therefore you don't need -v option here. –  rush Jul 5 '12 at 19:38
2  
not with the extra + at the end. –  lynxlynxlynx Jul 5 '12 at 22:33
1  
@user946850 every time I write find -exec \+ somebody writes me that it's much better to use xargs. why nobody looks man before writing a comment? (: –  rush Jul 6 '12 at 5:03
3  
@user946850 -exec ... {} \+ is not equivalent to xargs. Please read the findutils documentation! (I worked quite hard on it!) –  James Youngman Jul 6 '12 at 14:57

You've almost got it really...

find . -type f -iname "*.java" -print0 | xargs -0 grep -v "something something"

The dot '.' says to start from here. (yours implies it.. but never assume).

-iname uses case-insensitive search, just in case (or just in no-case).
-print0 sends filenames to xargs with a trailing \x00 character, which prevents problems with filenames having spaces in them.

the '-0' on xargs says to expect filenames ending with \x00 instead of returns.

and your grep command...

Pretty much got it.


EDIT::

From your update:

find . -type f -iname "*pb.java" -print0 | xargs -0 grep -iL "something"

should help. (Added -L from @rush's answer, good job)

I get the idea that your grep needs either the '-i' option, or to be less explicit.

Try the command in parts... does THIS output filenames that seem proper?

find . -type f -iname "*pb.java"

If so, then your problem is likely either your grep search pattern is not matching (spelling error? it happens!), or there just aren't any matches.

Absolute worst case:

grep -riL "something" *

will do a LOT more work searching everything, but should give you some output.

share|improve this answer
    
I tried your modifications and I'm still not getting the result I am expecting. I'll try updating the question to make it clearer. –  Hyangelo Jul 5 '12 at 19:14
    
If the author of the question just wants to find filenames, shouldn't the grep be 'grep -l -v'? –  Bruce Ediger Jul 5 '12 at 19:14
    
he's looking for specific contents in the *.java files –  lornix Jul 5 '12 at 19:17
    
xargs -0 grep -v "something something" should be xargs -0 grep -v "something something" /dev/null otherwise you will get odd results when find produces no matching files. –  James Youngman Jul 6 '12 at 14:58
    
{Grin} yeah, somewhere in there the logic got all hairy. Nothing like an inversed false logic multiple test to make your head hurt. –  lornix Jul 6 '12 at 15:00

The computer is being a computer: it's doing what you told it to do instead of what you wanted it to do.

grep -v "something something" prints all lines that do not contain something something. For example, it prints two lines among the following three:

hello world
this is something something
something else

To print files that do not contain extends SomethingSomething anywhere, use the -L option:

grep -L -E 'extends[[:space:]]+SomethingSomething' FILENAME…

Some versions of grep do not have the -L option (it is not specified by POSIX). If yours doesn't, have it print nothing, and use the return code to have the calling shell do whatever it should do instead.

grep -q -E 'extends[[:space:]]+SomethingSomething' FILENAME ||
echo "$FILENAME"

Alternatively, use awk.

awk '
    FNR == 1 && NR != 1 && !found { print fn }
    FNR == 1 { fn = FILENAME; found = 0; }
    /extends[[:space:]]+SomethingSomething/ { found = 1 }
    END { if (fn != "" && !found) print fn }
'

On Linux or Cygwin (or other system with GNU grep), you don't need to use find, as grep is capable of recursing.

grep -R --include='*.java' -L -E 'extends[[:space:]]+SomethingSomething'

If your shell is ksh or bash or zsh, you can make the shell do the filename matching. On bash, run set -o globstar first (you can put this in your ~/.bashrc).

grep -L -E 'extends[[:space:]]+SomethingSomething' **/*.java
share|improve this answer
    
Wow this is even better. I was using 'extends(/s)+SomethingSomething' which seemed to work as far as I could tell. Is there any difference with this syntax with the one specified in the Extended RegEx version? –  Hyangelo Jul 6 '12 at 12:06
    
@Hyangelo \s is a GNU grep extension, which I think is synonymous with [[:space:]]. –  Gilles Jul 6 '12 at 17:40

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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