Loop recursivamente através de diretórios e executar um comando em um arquivo no diretório

6

Estou tentando descompilar todas as classes Java contidas em um determinado diretório, começando na parte superior do diretório.

O diretório tem muitos subdiretórios e a profundidade de cada um deles é diferente.

O que eu quero fazer é entrar em cada diretório e se o conteúdo for um arquivo com sufixo .class , execute o comando decompiler, que criará um arquivo decompilado no mesmo diretório.

Eu tentei o seguinte, mas eles não conseguem o que eu sou depois.

O primeiro método foi:

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

O problema aqui é que não é recursivo. Talvez isso possa ser ajustado de maneira que seja recursivo?

O segundo método foi muito mais simples, mas gera os arquivos no diretório principal. Enquanto isso pode ser ok, infelizmente, eu tenho classes que têm os mesmos nomes, mas em pacotes diferentes, então esta solução não funciona:

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

Um destes pode ser ajustado para conseguir o que eu quero? Como o primeiro comando pode ser convertido em uma operação recursiva?

    
por Ares 04.09.2016 / 04:48

3 respostas

0

Eu encontrei uma solução que funcionou para mim:

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

Nenhuma declaração if necessária para verificar o tipo correto de arquivo porque o jad não executará a descompilação se o arquivo não for um arquivo de classe.

    
por 04.09.2016 / 05:34
3

Eu não sou 100% sólido em como o JAD funciona exatamente, mas com base em as informações que encontrei em este arquivo README , este comando find deve dar a você um começo:

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

A opção -s definirá a extensão de saída como .java e a -d definirá um diretório de destino para saída de arquivo com base no local em que o arquivo .class original foi encontrado por meio de find . A chave para resolver problemas como este é entender que você não é a primeira pessoa que deseja enviar a saída da linha de comando para outro destino.

    
por 04.09.2016 / 05:14
1
O

jad tem suporte para este , de acordo com a documentação . Ele expandirá globs para você, incluindo ** recursive globs. Portanto, use aspas para protegê-las do shell, como neste exemplo cmd copiado dos documentos:

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.

Esta é quase certamente a melhor opção se você gosta de suas opções de estrutura de diretórios. Parece que sem -r não vai transformar a estrutura de pacotes em diretórios. IDK se você conseguisse colocar os arquivos .java próximos aos arquivos .class com sua estrutura de diretório.

Usando find -execdir para executar o jad no diretório correto :

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

Isso será efetivamente cd para cada diretório que contenha pelo menos um .class e faça o equivalente a executar jad *.class . (Observe o + em vez de \; para agrupar vários argumentos em um comando).

GNU find é poderoso; leia a página de manual .

Ou use recursos bash :

Como você está usando o bash, você pode tornar for dir in ./* recursiva com o recurso globstar do bash. É significativamente mais lento que find para árvores de diretórios grandes, porque bash não sabe aproveitar a dica de tipo de arquivo retornada por readdir para evitar ter que lstat toda entrada de diretório para ver se é um diretório. Mas bash recursive globs pode ser útil.

# 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

Observe o uso de uma barra à direita para tornar os glob apenas diretórios correspondentes.

    
por 04.09.2016 / 10:47