6

I am trying to decompile all Java classes contained in a certain directory, starting at the top of the directory.

The directory has many subdirectories and the depth of each one differs.

What I want to do is go into each directory and if the content is a file with suffix .class, run the decompiler command, which will create a decompiled file in the same directory.

I’ve tried the following but they don’t achieve what I am after.

The first method was:

for dir in ./*; do ( ( if [[ -d $dir ]]; then cd "$dir"; fi ) && ( for f in "$dir"; do ( if [[ -f $f && $f == *".class"* ]]; then jad "$f"; fi ); done ) ); done

The problem here is that it is not recursive. Maybe this can be tweaked in a way that it would be recursive?

The second method was much simpler, but it outputs the files in the top directory. While this might be ok, unfortunately, I have classes that have the same names, but in different packages, so this solution does not work either:

find . -type f -name "*.class" | while read filename; do echo $filename; done

Can one of these be tweaked to achieve what I want? How can the first command be converted to a recursive operation?

3

I’m not 100% solid on how JAD works exactly, but based on the info I found in this README file, this find command should give you a start:

find . -type f -name '*.class' |\
  while IFS= read -r java_class_path
  do
    java_dirname=$(dirname "${java_class_path}")
    jad -sjava -d"${java_dirname}" "${java_class_path}"
  done

The -s option will set the output extension to .java and the -d sets a destination directory for file output based on where the original .class file was found via find. The key to solving problems like this is to understand you are not the first person who wanted to output command line output to another destination.

  • I think you've misinterpreted my question. I am looking to decompile all classes contained within a certain directory. – Mars Sep 4 '16 at 3:38
  • @Joker I’m not too sure how I might have misinterpreted this. This command searches all directories starting from the top, finds .class files and then runs a command that will output the decompiled output of JAD to the parent directory of that .class file. – JakeGould Sep 4 '16 at 3:43
  • 1
    My mistake, I hadn't looked through your answer well enough. It works as intended, thanks. – Mars Sep 4 '16 at 4:00
  • 1
    You should use while IFS= read -r class or something. It's good practice to only use upper-case names for environment vars, and setting IFS + using -r avoids some problems. There are other better ways to solve this problem though, e.g. mine which only pays jad's startup overhead once per directory instead of once per file. (Or once for the whole tree if jad's built-in ** expansion does what the OP needs). – Peter Cordes Sep 4 '16 at 8:53
1

jad has support for this built-in, according to the documentation. It will expand globs for you, including ** recursive globs. So use quotes to protect them from the shell, like this example cmd copied from the docs:

jad -o -r -sjava -dsrc 'tree/**/*.class'

This command decompiles all .class files located in all subdirectories of tree and creates output files in subdirectories of src according to package names of classes. For example, if file tree/a/b/c.class contains class c from package a.b, then output file will have a name src/a/b/c.java.

This is almost certainly the best option if you like its choices of directory structure. I looks like without -r it won't turn package structure into directories. IDK if you could get it to put the .java files next to the the .class files with your directory structure.


Using find -execdir to run jad in the right directory:

# untested
find [more find options]  -name '*.class' -execdir jad {} +

This will effectively cd to every directory that contains at least one .class, and do the equivalent of running jad *.class there. (Note the + instead of \; to group multiple args into one command).

GNU find is powerful; read the man page.


Or use bash features:

Since you're using bash, you can make for dir in ./* recursive with bash's globstar feature. It's significantly slower than find for big directory trees, because it bash doesn't know to take advantage of the file-type hint returned by readdir to avoid having to lstat every dir entry to see if it's a directory. But bash recursive globs can be useful.

# untested
shopt -s globstar       # put this in your .bashrc if you want it enabled all the time

for dir in ./**/; do
    pushd "$dir"
    classfiles=( *.class )               # expand the glob into an array
    [[ -e $classfiles ]] &&              # test the first element of the array.  Will fail if *.class didn't match anything.
       jad *.class
    popd "$dir"
done

Note the use of a trailing slash to make the glob match only directories.

0

I’ve come up with a solution that worked for me:

find . -type d | while read dir; do ( if [[ -d $dir ]]; then cd $dir && find . -maxdepth 1 -type f | while read f; do ( jad $f ); done fi ); done

No if statement necessary, to check for the correct type of file because jad will not perform the decompile if the file is not a class file.

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.