Recorra recursivamente los directorios y ejecute un comando en un archivo en el directorio

6

Estoy tratando de descompilar todas las clases de Java contenidas en un determinado directorio, 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, ejecute el comando descompilador, que creará un archivo descompilado en el mismo directorio.

He intentado 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. ¿Tal vez esto se pueda modificar de una manera que sea recursiva?

El segundo método era 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 diferentes paquetes, por lo que esta solución tampoco funciona:

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

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

Marte
fuente

Respuestas:

3

No soy 100% sólido sobre cómo funciona JAD exactamente, pero según la 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 en .javay -destablece un directorio de destino para la salida del archivo en función de dónde .classse encontró el archivo original find. La clave para resolver problemas como este es comprender que usted no es la primera persona que quería enviar la salida de la línea de comandos a otro destino.

JakeGould
fuente
Creo que has malinterpretado mi pregunta. Estoy buscando descompilar todas las clases contenidas en un determinado directorio.
Marte
@Joker No estoy muy seguro de cómo podría haber malinterpretado esto. Este comando busca en todos los directorios desde la parte superior, encuentra .classarchivos y luego ejecuta un comando que generará la salida descompilada de JAD en el directorio principal de ese .classarchivo.
JakeGould
1
Mi error, no había examinado su respuesta lo suficientemente bien. Funciona según lo previsto, gracias.
Marte
1
Deberías usar while IFS= read -r classo algo. Es una buena práctica usar solo nombres en mayúsculas para variables de entorno, y configurar IFS + usando -revita algunos problemas . Sin embargo, hay otras formas mejores de resolver este problema, por ejemplo, la mía que solo paga los gastos generales de inicio de jad una vez por directorio en lugar de una vez por archivo. (O una vez para todo el árbol si la **expansión incorporada de jad hace lo que el OP necesita).
Peter Cordes
1

jad tiene soporte para este incorporado , de acuerdo con la documentación . Expandirá los globos para ti, incluidos **los globos recursivos. Por lo tanto, 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 treey crea archivos de salida en subdirectorios srcsegún los nombres de paquetes de las clases. Por ejemplo, si el archivo tree/a/b/c.classcontiene la clase cdel paquete a.b, el archivo de salida tendrá un nombre src/a/b/c.java.

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


Utilizando find -execdirpara ejecutar jad en el directorio correcto :

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

Esto será efectivo cdpara cada directorio que contenga al menos uno .class, y haga el equivalente de ejecutarse jad *.classallí. (Tenga +en cuenta que en lugar de \;agrupar múltiples argumentos en un solo comando).

GNU findes poderoso; lea la página del manual .


O use las funciones de bash :

Como estás usando bash, puedes hacer for dir in ./*recursivo con la función globstar de bash. Es significativamente más lento que findpara los grandes árboles de directorios, porque bash no sabe aprovechar la sugerencia de tipo de archivo devuelta readdirpara evitar tener que acceder a lstatcada entrada de directorio para ver si es un directorio. Pero los globos recursivos bash pueden ser ú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 inclinada final para que el globo solo coincida con los directorios.

Peter Cordes
fuente
0

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 se ifnecesita 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.

Marte
fuente