Comando TYPE do cmd trava e não retorna ao java

14 respostas
E

Olá mestres,

Estou com um problemão…
Numa parte do meu código eu tenho que concatenar vários arquivos, poderia fazer com um FileWriter, mas com o comando TYPE do cmd fica muito mais rápido.
Desenvolvi todo o sistema e testei com uma amostra de uns 50MB de arquivos e funcionou, então entreguei o sistema.
Mas em produção, com uns 1GB-2GB de arquivos ele travou…ficou a madrugada toda e o arquivo concatenado não sai de 28KB, e nem retorna pro código java…
Printei o comando que estou dando e joguei no prompt, exatamente como está, e não levou nem 10 min!!!

Estranho porque eu achei que o java simplesmente chamava o cmd e aguardava retorno, então porque funciona no prompt e não funciona chamando pelo java?

Segue o código:

StartTratadorValidosDetraf.getLogger().info("Concatenando arquivos TCO");
String comando = "cmd /c TYPE \"" 
	+ StartTratadorValidosDetraf.getOutput().getAbsolutePath() + "\\*.TCO\" > " 
	+ "\"" + StartTratadorValidosDetraf.getOutput().getAbsolutePath() + "\\validos_detraf_tco.txt\"";
System.out.println(comando);
Process procTco = Runtime.getRuntime().exec(comando);
procTco.waitFor();
if(procTco.exitValue() != 0)	{
	StartTratadorValidosDetraf.getLogger().fatal("Ocorreu um erro ao concatenar os arquivos TCO");
	System.exit(3);
}

No System.out.println() imprime: cmd /c TYPE "D:\Programacao\BV-R2\detraf\validos\work*.TCO" > "D:\Programacao\BV-R2\detraf\validos\work\validos_detraf_tco.txt"

Segura essa!

14 Respostas

V

Oi Eric, usar comandos nativos normalmente nao eh uma boa ideia por causa da portabilidade mas, por que voce nao tenta entao com o comando COPY/B que eh usado justamente para esse proposito? Nunca testei com arquivos tao grandes mas, acho que vai sair melhor que redirecionar uma saida do TYPE para um arquivo com >.

A sintaxe seria assim: copy/b arquivo1+arquivo2…arquivoN arquivoDestino

ate+

E

Obrigado pela ajuda…

Usei o COPY, mas deu na mesma…funciona no prompt mas trava em 28KB no Java…

Pelo jeito vou ter que usar FileWriter mesmo…mas fica muito lento, porque são milhares de arquivos de 1KB e alguns pouco maiores…
Será que o Java 7 tem algo melhor pra concatenar arquivos? Sei que teve algumas mudanças boas na manipulação de arquivos…

Não estou preocupado com portabilidade porque é uma coisa “provisória”…rs

V

Ah entendi, bom voce pode tentar executar de outra forma, crie pelo java ou com o notepad do windows um arquivo .bat com os comandos de concatenacao que voce quer e use a classe Desktop para executa-lo.

Desta forma voce vai informar ao sistema operacional que quer executar o arquivo .bat e ele vai chamar o cmd.exe, ao invez de voce chamar diretamente o cmd.exe com parametros.

File file = new File(seuArquivoBat);
		if (file.exists()) {
			java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
			try {
				desktop.open(file);
				return "Pedido de execucao de " + file.getAbsolutePath() + " enviado para SO.";
			} 
			catch (IOException e) {
				return "Nao foi possivel abrir o arquivo " + seuArquivoBat;
			}
		}
		else {
			return "Arquivo nao encontrado: " + seuArquivoBat;
		}

Eh uma tentativa espero que de certo.

M

A classe Process tem problemas quando os comandos executados usam subchamadas, então ele perde a referencia e as vezes ele sai do waitFor sem terminar ou fica travado.
No doc explica por que isso acontece.

[]'s

E

Legal, eu não conhecia a classe Desktop.

O problema é que usando ela eu não vou saber quando o meu COPY terminou…ela não tem um “waitFor()”
Eu preciso esperar a concatenação terminar para carregar o arquivo no banco.

Mauricio, o estranho é que quando eu faço com 50MB ele não trava…e o comando TYPE ou COPY são nativos do cmd, então o cmd só vai sinalizar o seu término quando terminar a concatenação, por isso usamos o /c depois do cmd na string de comando. Não acredito que isso seja uma subchamada…eu acho.

O jeito é usar o FileWriter mesmo, ou uma das classes do pacote nio (FileChannel, etc) que acredito que seja mais rápido.

Valeu pessoal, fiquei chateado com mas mesmo assim muito obrigado.

E

Você precisa fazer uma cópia concatenada rápida e ainda por cima ter uma indicação de porcentagem (talvez para usar com um JProgressBar?)

E

Não…eu não preciso de indicação de porcentagem…
Eu só precisava que o meu código esperasse até a concatenação acabar.
Se eu usar a classe Desktop ou usar Process sem pra rodar uma bat, ele executa a bat e continua executando o resto do código, ele não espera a concatenação acabar…
E eu preciso usar o arquivo da concatenação pra carregar no banco, no mesmo código.

Obs.: Essa carga no banco é feito com um script do banco Teradata, e ele só aceita um arquivo, por isso eu preciso concatenar pra evitar milhares de conexões…

E

Sei. O comando do Windows que você poderia usar para concatenar diversos arquivos (são arquivos-texto, não?) seria algo como:

copy /y /a arquivo1+arquivo2+arquivo3+arquivo4   arquivodestino

O “/y” faz com que, se o arquivo destino já existir por algum motivo, ele não fique perguntando se quer sobreescrever (e provavelmente foi por causa disso que seu programa não funcionou direito).

V

eric para voce saber quando o processamento do seu .bat terminar voce pode colocar na ultima linha do seu .bat o comando:

echo terminou > fim.tmp

assim voce coloca no seu codigo java um while esperando a criacao do arquivo fim.tmp quando ele for criado voce termina seu while e continua seu codigo.

ah… nesse caso eh melhor voce colocar na primeira linha do seu .bat o comando del fim.tmp para o fim.tmp

E

hehe…vicentepaf, eu não posso negar, eu tinha a algum tempo atrás alguns processos em produção que usavam esse esquema ai, é um método prático e que funciona, mas estava tentando evitar isso, meu líder costuma medir a qualidade do software pela quantidade de arquivos que ele usa…rs

entanglement, nem vai dar pra testar mais, porque quando vi sua dica eu já tinha desfeito tudo e feito com FileWriter, por sorte mudaram alguns requisitos e eu tinha que remover o cabeçalho dos arquivos, então acabou que tive que reescrever o arquivo mesmo…

mas ainda assim não acho que seja o /y…talvez o COPY realmente não funcionasse por conta disso, mas o TYPE também não funcionava, e ele não pede confimação…

Galera, meus sinceros agradecimentos, até o próximo desafio.

E

Se eu soubesse disso, tinha dado a dica de usar um new BufferedWriter (new FileWriter) - e um new BufferedReader (new FileReader) com um valor grande para os buffers de leitura e escrita. Isso faz com que a leitura e escrita seja bem mais rápida. Tipicamente usar 8MB é um bom valor.

E

pow cara…sempre usei o FileReader/FileWriter pra arquivos de texto…não sabia que o fato de bufferizando eles eu teria ganho na performance…
Vou dar uma olhada nisso…o meu processo está tratando uns 6GB de arquivos e está levando quase uma hora pra fazer…sem contar que deixa a máquina lenta, só não sei se está lento por causa da leitura/escrita dos arquivos ou se é por causa da descompactação que faço antes de tratar o arquivo…descompacto o unzip do java mesmo…

Não é um problema que me deixe preocupado, mas só por conhecimento e uma possível melhoria: Bufferizar deixa realmente mais rápido?

É o que diz no javadoc do BufferedReader, mas não sei se quer dizer que ele é mais rápido que um FileWriter/Reader puro…

E

Basicamente é o seguinte: se você usar um BufferedReader/BufferedWriter, você irá diminuir a quantidade de vezes que as APIs do sistema operacional (como read/write no Unix/Linux ou ReadFile/WriteFile no Windows) são chamadas pelo FileReader/FileWriter. Isso porque ele pode ler uma quantidade maior de bytes de cada vez se o buffer for aumentado.

E

entanglement, meus parabéns, obrigado por compartilhar seu conhecimento com todos.

Ficaria mais ou menos assim então certo?
Os 8MB ficariam daquele jeito? Nem testei o código ainda, mas acho que é isso, certo?

public void concatenaArquivos() throws IOException {
		  
		BufferedWriter buffOut = new BufferedWriter(new FileWriter(StartTratadorExcluidosDetraf.getOutput() + "\\validos_detraf_to_mload.txt"), 8 * 1024 * 1024);
		BufferedReader buffIn = null;
		String linha;
		int cont = 0;
		
		for(File arquivo : StartTratadorExcluidosDetraf.getInput().listFiles())	{
			
			buffIn = new BufferedReader(new FileReader(arquivo), 8 * 1024 * 1024); // acho que isso são 8MB
			cont = 0;
			
			while((linha = buffIn.readLine()) != null)	{   
				if(cont++ > 0)	{ //Pulo o cabeçalho
					buffOut.write(linha);
				}
			}
		}
		
		buffIn.close();
		buffOut.flush();
		buffOut.close();
	}

Valeu mesmo.

Criado 19 de dezembro de 2011
Ultima resposta 21 de dez. de 2011
Respostas 14
Participantes 4