Bash – Looking for a Java class in a set of JARs with find, unzip, grep

bashcommand linefindgrepjava

I was trying to find the JAR containing a Java Class. JARs are in zip format.

My first attempt was:

$ find -name "*3.0.6.RELEASE.jar" | xargs -l1 unzip -l \
    | grep stereotype.Controller
      554  2011-08-18 16:49   org/springframework/stereotype/Controller.class
      554  2011-08-18 16:49   org/springframework/stereotype/Controller.class

I found the class, but I still don't know which of the 25 matching files contains it (there are two JARs containing it). So I thought to use tee in the middle to output the file names.

$ find -name "*3.0.6.RELEASE.jar" | tee - | xargs -l1 unzip -l \
    | grep stereotype.Controller
  554  2011-08-18 16:49   org/springframework/stereotype/Controller.class
  554  2011-08-18 16:49   org/springframework/stereotype/Controller.class
  554  2011-08-18 16:49   org/springframework/stereotype/Controller.class
  554  2011-08-18 16:49   org/springframework/stereotype/Controller.class

I'd have expected to see a filename followed by the Controller.class for matching files and by the next filename for non mathing. However, now that I think about it, standard output just flows in the pipe and gets processed by xargs, so it makes sense.

I could use standard error, but then, I suppose, that since the processes are running concurrently, I could have timing issues that make the output not in the order I would hope to see.

So there must be a better way to approach this problem, anyone has ideas?

UPDATE: While waiting for an answer, I wrote a horrible Perl one liner that does the trick, but looking forward to see more elegant solutions.

$ find -name "*3.0.6.RELEASE.jar" | perl -e 'while (<>) { \
    $file=$_; @class=`unzip -l $_`; foreach (@class) { \
    if (/stereotype.Controller/) {print "$file $_";} } }'

Output:

./spring-context/3.0.6.RELEASE/spring-context-3.0.6.RELEASE.jar
   554  2011-08-18 16:49   org/springframework/stereotype/Controller.class
./org.springframework.context/3.0.6.RELEASE/org.springframework.context-3.0.6.RELEASE.jar
   554  2011-08-18 16:49   org/springframework/stereotype/Controller.class

Best Answer

Try this:

find -name "*3.0.6.RELEASE.jar" -exec sh -c 'unzip -l "{}" | grep -q stereotype.Controller' \; -print

There's no need for xargs or a for loop here. All can be done with a single find. If you want to also output the content that got grepped, just remove the -q option to grep - but notice that the grep matches will appear before each file name. For a clearer output, you can add -exec echo \; at the very end.

Related Question