7

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

3 Answers 3

9

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.

1
  • +1, I always wanted to see how buffered items from find can be passed on to two items. Good stuff!! Commented Nov 28, 2011 at 16:14
2

In your 1st one-liner, in the grep command add -H option. That is to include the file name in the result.

Here is from the man-page -

-H, --with-filename
              Print the filename for each match.</strike>

UPDATE

May be this script will help out -

#!/bin/bash

searchSTR="YOUR SEARCH"

for i in `find . -name "*jar"`
do
  echo "Scanning $i ..."
  jar tvf $i | grep $searchSTR > /dev/null
  if [ $? == 0 ]
  then
    echo "==> Found \"$searchSTR\" in $i"
  fi
done

One-Liner:

for i in `find . -name "*.jar"`; do jar tvf $i | grep "search pattern" && echo $i ; done

The only sad part is the name of the jar file will be displayed after the grep content

5
  • 1
    Thank you, but doesn't help, since it's already processed with unzip, is too late to get the file name. Here is the output: (standard input): 554 2011-08-18 16:49 org/springframework/stereotype/Controller.class
    – stivlo
    Commented Nov 28, 2011 at 8:10
  • I saw the update, it's conceptually similar to my Perl, I think I will probably change it to accept a parameter for the search string, look into my .m2 Maven repo, and save the script (well formatted and commented instead of the one liner) in /usr/local/bin/searchJar. I will do it in the next few days, unless someone will tell me that there is a simpler solution.
    – stivlo
    Commented Nov 28, 2011 at 9:15
  • That sounds right, you can make this into a one-liner but because we need to look inside the jar file, I don't really know a way of doing grep without jar tvf. The first echo can be removed. I just added it so that you can quickly browse through the jar files, our find command identifies. Commented Nov 28, 2011 at 9:19
  • Added a one-liner. Hope that helps! Commented Nov 28, 2011 at 9:29
  • @jaypalsingh if you really found the printing order sad, maybe a slight variation of your code could suffice to render you happy again: SEARCH="Logger"; time while read jar _; do resultset=$(grep "${SEARCH}.*.class" < <(tar tf $jar)); if [ ${#resultset} -gt 0 ]; then printf "%s\n%s\n" "--> Found $SEARCH in $jar:" "$resultset"; fi; resultset=""; done < <(find . -type f -name "*.jar"). It's even mighty fast ;)
    – ikaerom
    Commented Feb 6, 2014 at 20:36
1

An old thread but this looks like a cleaner way to get this done: http://alumnus.caltech.edu/~leif/opensource/cpcheck/CpCheckApp.html

Windows:
    cpcheck [flags] classpath [classpath]*
Unix:
    ./cpcheck.sh [flags] classpath [classpath]*
Java:
    java -jar cpcheck.jar [flags] classpath [classpath]*
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .