Рекурсивный проход по каталогам и выполнение команды для файла в каталоге

Рекурсивный проход по каталогам и выполнение команды для файла в каталоге

Я пытаюсь декомпилировать все классы Java, содержащиеся в определенном каталоге, начиная с верхней части каталога.

Каталог имеет множество подкаталогов, глубина которых различна.

Мне нужно зайти в каждый каталог и, если содержимое представляет собой файл с суффиксом .class, запустить команду декомпилятора, которая создаст декомпилированный файл в том же каталоге.

Я пробовал следующие варианты, но они не дали мне желаемого результата.

Первый метод был таким:

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

Проблема в том, что это не рекурсивно. Может быть, это можно подправить так, чтобы это было рекурсивно?

Второй метод был намного проще, но он выводит файлы в верхнем каталоге. Хотя это может быть нормально, к сожалению, у меня есть классы с одинаковыми именами, но в разных пакетах, поэтому это решение тоже не работает:

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

Можно ли настроить один из них так, чтобы он соответствовал моим требованиям? Как можно преобразовать первую команду в рекурсивную операцию?

решение1

Я не уверен на 100% в том, как именно работает JAD, но основываясь наинформация, которую я нашел в этом файле README, эта findкоманда должна вам помочь начать:

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

Параметр -sустановит выходное расширение на .javaи задаст целевой каталог для выходного файла на основе того, где был найден -dисходный файл через . Ключ к решению таких проблем — понимать, что вы не первый человек, который захотел вывести вывод командной строки в другое место назначения..classfind

решение2

jad имеет встроенную поддержку для этого,согласно документации. Он расширит для вас globs, включая **рекурсивные globs. Поэтому используйте кавычки, чтобы защитить их от оболочки, как в этом примере cmd, скопированном из документации:

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

Эта команда декомпилирует все файлы .class, расположенные во всех подкаталогах treeи создает выходные файлы в подкаталогах srcв соответствии с именами пакетов классов. Например, если файл tree/a/b/c.classсодержит класс cиз пакета a.b, то выходной файл будет иметь имя src/a/b/c.java.

Это почти наверняка лучший вариант, если вам нравится его выбор структуры каталогов. Мне кажется, что без -rнего структура пакетов не будет преобразована в каталоги. IDK, если вы могли бы заставить его размещать .javaфайлы рядом с файлами .classс вашей структурой каталогов.


Использование find -execdirдля запуска jad в правильном каталоге:

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

Это фактически перейдет cdк каждому каталогу, содержащему хотя бы один .class, и выполнит эквивалент запуска jad *.classтам. (Обратите внимание на использование +вместо , \;чтобы сгруппировать несколько аргументов в одну команду).

GNU find— мощный инструмент; читайтестраница руководства.


Или используйте возможности bash:

Поскольку вы используете bash, вы можете сделать for dir in ./*рекурсивный с помощью функции bash globstar. Это значительно медленнее, чем findдля больших деревьев каталогов, потому что bash не знает, что нужно использовать подсказку о типе файла, возвращаемую , readdirчтобы избежать необходимости lstatпроверять каждую запись dir, является ли это каталогом. Но рекурсивные globs bash могут быть полезны.

# 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

Обратите внимание на использование завершающего слеша, чтобы глобальный шаблон соответствовал только каталогам.

решение3

Я нашел решение, которое мне помогло:

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

ifДля проверки правильности типа файла не требуется никаких инструкций, поскольку jad не будет выполнять декомпиляцию, если файл не является файлом класса.

Связанный контент