Recorre directorios de forma recursiva y ejecuta un comando en un archivo en el directorio

Recorre directorios de forma recursiva y ejecuta un comando en un archivo en el directorio

Estoy intentando descompilar todas las clases de Java contenidas en un directorio determinado, comenzando en la parte superior del directorio.

El directorio tiene muchos subdirectorios y la profundidad de cada uno es diferente.

Lo que quiero hacer es ir a cada directorio y si el contenido es un archivo con sufijo .class, ejecutar el comando del descompilador, que creará un archivo descompilado en el mismo directorio.

Intenté lo siguiente pero no logran lo que busco.

El primer método fue:

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

El problema aquí es que no es recursivo. ¿Quizás esto pueda modificarse de manera que sea recursivo?

El segundo método fue mucho más simple, pero genera los archivos en el directorio superior. Si bien esto podría estar bien, desafortunadamente tengo clases que tienen los mismos nombres, pero en paquetes diferentes, por lo que esta solución tampoco funciona:

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

¿Se puede modificar uno de estos para lograr lo que quiero? ¿Cómo se puede convertir el primer comando en una operación recursiva?

Respuesta1

No estoy 100% seguro de cómo funciona JAD exactamente, pero basándome enla información que encontré en este archivo README, este findcomando debería darle un comienzo:

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

La -sopción establecerá la extensión de salida .javay -destablecerá un directorio de destino para la salida del archivo según dónde .classse encontró el archivo original a través de find. La clave para resolver problemas como este es comprender que no es la primera persona que desea enviar la salida de la línea de comando a otro destino.

Respuesta2

jad tiene soporte para esto incorporado,según la documentación. Expandirá los globos por usted, incluidos **los globos recursivos. Así que use comillas para protegerlos del shell, como este ejemplo cmd copiado de los documentos:

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

Este comando descompila todos los archivos .class ubicados en todos los subdirectorios de treey crea archivos de salida en los subdirectorios srcde acuerdo con los nombres de los paquetes de las clases. Por ejemplo, si el archivo tree/a/b/c.classcontiene una clase cdel paquete a.b, entonces el archivo de salida tendrá un nombre src/a/b/c.java.

Es casi seguro que esta es la mejor opción si le gustan las opciones de estructura de directorios. Parece que sin -rél no se convertirá la estructura del paquete en directorios. IDK si pudiera lograr que coloque los .javaarchivos al lado de los .classarchivos con su estructura de directorios.


Usando find -execdirpara ejecutar jad en el directorio correcto:

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

Esto afectará efectivamente cda cada directorio que contenga al menos un archivo .class, y hará el equivalente a ejecutarlo jad *.classallí. (Tenga en cuenta que +en lugar de \;agrupar varios argumentos en un solo comando).

GNU findes poderoso; leerla página de manual.


O usar funciones de bash:

Como estás usando bash, puedes hacerlo for dir in ./*recursivo con la función globstar de bash. Es significativamente más lento que findpara árboles de directorios grandes, porque bash no sabe aprovechar la sugerencia de tipo de archivo devuelta readdirpara evitar tener que revisar lstatcada entrada del directorio para ver si es un directorio. Pero los globos recursivos de bash pueden resultar útiles.

# 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

Tenga en cuenta el uso de una barra diagonal para que el globo coincida solo con directorios.

Respuesta3

Se me ocurrió una solución que funcionó para mí:

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 ifes necesaria ninguna declaración para verificar el tipo correcto de archivo porque jad no realizará la descompilación si el archivo no es un archivo de clase.

información relacionada