Como detectar o nome da classe principal de um arquivo java com várias classes usando o shell script?

0

Eu faço um script javaFilesConcatenator usando cat e sed , que concatenam vários arquivos .java :

#!/bin/bash
########################################################################################################################
# This file create a "MainFileName.java.output" file concatenating all java file in this directory.
# Run this script as '$ ./javaFilesConcatenator MainFileName' when "MainFileName.java" contains the main method.
########################################################################################################################

cat *.java | sed -e 's/public class/class/g;s/public interface/interface/g' >$1.java.output
sed -i "s/class $1/public class $1/g" $1.java.output

Para executar este script, eu uso o nome da classe principal como argumento de entrada do usuário, porque a classe principal deve ser public e os outros não. Eu substituo todos os public class ClassName a class ClassName exceto a classe principal.

Agora, quero modificar meu script para torná-lo mais amigável ao usuário, para que ele detecte a classe principal sem fornecer o nome da classe principal. Eu acho que seria possível, por causa da classe principal é diferente dos outros. Ele contém um método main .

E, outro requisito é que ele tenha que colocar toda a instrução import (ig import java.util.Scanner; ) no início do documento cortando de posição diferente (cada arquivo tem uma declaração import no início, após concatenar estes não estão todos no começo.) do arquivo de saída.

Outro requisito opcional é remover todos os package com.alhelal.texpad; da linha, mas não os comentários com a palavra package .

Como posso satisfazer meus requisitos?

    
por alhelal 26.10.2017 / 03:11

2 respostas

1

Esse script sed deve seguir todas as etapas, mas não consegui testá-lo, porque não tenho dados de teste:

sed '/^[^/]*package/d
     /import  *java\./{p;d;}
     H;/void main()/{g;s/.*\n[[:space:]]*public/& public/;h;}
     $!d;g;s/public class/class/g' *.java

Os principais conceitos do script:

  • Você pode passar vários nomes de arquivos para sed ; eles são lidos um após o outro, linha por linha. sed não saberá quando um arquivo ands e outro começarem, mas não precisamos disso aqui de qualquer forma.
  • Como sed funciona linha por linha, precisamos de alguma memória para armazenar as linhas que precisamos depois. Isso é necessário para recorrer ao arquivo (todas as importações na parte superior da saída). Em sed , essa memória é o espaço de espera; pense nisso como uma prancheta. Enquanto o espaço de padrão é substituído pela nova linha com cada ciclo do script, o buffer de retenção permanece inalterado. Você pode copiar o espaço de padrão para o espaço de espera com h ou copiar de volta com g ou trocar ambos com x . Com H , o conteúdo do espaço de padrão é anexado ao espaço de espera (com uma nova linha no meio), com g vice-versa. Assim, as linhas que queremos no topo da saída são impressas imediatamente (com p ), enquanto todas as outras linhas são anexadas ao espaço de espera. Depois que chegarmos ao final de todas as linhas, as linhas coletadas no espaço de espera poderão ser processadas.
  • Todo public deve ser removido, exceto um. Eu faço isso com um truque: assim que eu encontrar o método main , eu sei que a última aula é a que permanece pública, então eu preciso marcá-la como tal. Mas como? O truque: eu adiciono um segundo public lá, então recebemos public class fooClass ... public public class thisIsTheMainClass ... public class barClass , então se depois substituirmos todos os public class por class , a classe principal ainda terá um atributo public restante. A adição do segundo public ocorre na terceira linha (precisamos copiar o espaço de armazenamento para o espaço de padrão para trabalhar com ele e depois copiá-lo de volta).
  • Uma coisa a ter em mente: você pode preceder os comandos sed com um "endereço", portanto, eles são executados apenas para linhas que correspondem ao endereço. Endereços podem ser um padrão regexp fechado em slahes para combinar com a linha; isso é usado nas três primeiras linhas do script. Ou pode ser um número de linha ou $ para a última linha, como na quarta linha do script, em que ! inverte o endereço selecionado, portanto, o d elete é executado para todas as linhas, mas o último . Se o endereço for seguido por comandos entre {} , todos os comandos internos serão executados apenas para esse endereço, como na segunda e terceira linha do script.

O script em detalhes:

  • /^[^/]*package/d exclui todas as linhas com a palavra package e nenhuma barra antes. Eu espero que isso seja o que você quer
  • /import *java\./{p;d;} imprime todas as linhas com as importações de java imediatamente e interrompe o processamento adicional.
  • H coleta todas as outras linhas no espaço de armazenamento em vez de imprimi-las. É assim que coletamos todas as importações no início da saída
  • /void main()/{g;s/.*\n[[:space:]]*public/& public/;h;} : Ao atender o método main , g transfere as linhas coletadas até o espaço padrão, o comando s pesquisa a última ocorrência de public no início de uma linha e acrescenta outro% código%. O public transfere o buffer modificado de volta para o espaço de espera
  • h Se esta não for a última linha, nenhuma outra ação
  • $!d move todas as linhas não importadas que coletamos para o espaço padrão e remove todas as g;s/public class/class/g' das classes. À medida que adicionamos um segundo public à classe principal, a classe principal permanecerá pública

Observe que normalmente você pode interromper scripts como esse com determinada entrada (palavras-chave em identificadores ou comentários).

    
por 26.10.2017 / 09:20
0

Primeiro, você deseja encontrar o método main, o nome de base do arquivo é o nome da classe

CLASS=$(basename -s .java $(grep -l 'public static void main' *.java))

Em seguida, extraia todas as importações, classifique-as e use uniq . Supondo que não haja importações de curinga que correspondam a outras importações.

sed -n '/^[ \t]*import /p' *.java | sort | uniq > $CLASS.java.output

Em seguida, processe os arquivos Java, extraindo as importações e os pacotes:

grep -hv '^[ \t]*import \|^[ \t]*package' *.java | sed -e 's/public class/class/g;s/public interface/interface/g' >>$CLASS.java.output

de outra forma, sed pura:

sed -e '/^[ \t]*\(import\|package\)/d;s/public class/class/g;s/public interface/interface/g' .java >>$CLASS.java.output

Então você quer reverter sua classe pública

sed -i "s/class $CLASS/public class $CLASS/g" $CLASS.java.output

Note que você não precisa usar bash no seu shebang ( #!/bin/sh é bom o suficiente), porque você não tem nenhum bashismo aqui.

Observe também que você não precisa cat-pipe to sed , sed -e 's/.../.../' *.java é o mesmo que cat *.java | sed -e 's/.../.../' , no entanto, você só tem sed em execução na primeira solução, não cat e sed - menos processos são sempre melhores.

    
por 26.10.2017 / 07:50