sábado, 25 de fevereiro de 2012

CommandLine - Uma classe Java para executar programas e comandos da linha de comando

Olá, programadores Java.

Estou desenvolvendo uma IDE em Java que seja simples e customizável, mas para isso precisava encontrar uma forma de executar programas externos a partir dela. Foi então que comecei a pesquisar sobre como fazer um programa Java executar qualquer comando no sistema operacional como se o usuário tivesse digitado direto na linha de comando (shell, intepretador de comandos, CMD.EXE, seja lá como preferir chamar) e pressionado Enter. Dessa forma, eu poderia usar isso para abrir qualquer programa (desde que eu soubesse o caminho de instalação dele) e executar qualquer comando do sistema.

Eis que descobri qual é o tal procedimento para conseguir isso, e então criei uma classe chamada CommandLine, para facilitar o uso desse valioso recurso que Java nos proporciona.

A classe CommandLine utiliza para este fim apenas 3 classes da API padrão de Java, a saber: Runtime, Process e String. Além destas, utiliza também um objeto de uma classe interna e privada chamada InputStreamHandler que foi criada para obter a saída textual do programa ou comando que for executado, isto é, tudo o que o programa que for executado enviar para os arquivos padrão stdout (mensagens de informação) e stderr (mensagens de erro).

Além disso, a interface da classe CommandLine é muito simples, permitindo executar comandos e obter a saída deles facilmente, bastando para isso instanciar um objeto e através dele utilizar os 4 métodos públicos fornecidos, que são os seguintes:

void execute( String command );

Executa o comando que foi passado no parâmetro String command. Esta string deve conter a linha inteira do comando incluindo todos os seus parâmetros, caso houver. Para executar um programa que esteja fora do diretório atual, você precisa passar o caminho completo do executável, a não ser que o usuário já tenha configurado o PATH do sistema para conter este caminho.

void execute( String[] cmdArray, String[] envp, File dir );

Executa o comando configurado no array de String cmdArray. Cada elemento desse array é um componente do comando, ou seja, o primeiro elemento é o próprio comando, os seguintes são parâmetros. O array de String envp pode ser null ou então deve conter a configuração de variáveis de ambiente. O último parâmetro deste método é um objeto File que pode conter o caminho do diretório a partir do qual o comando será executado, ou então pode ser null, e o caminho não será alterado antes de executar o comando (será usado o caminho atual).

String getStdinData();

Retorna uma string que contém todo o conteúdo textual (mensagens de informação) gerado pelo programa ou comando, após ser executado. Essa função sempre retorna o conteúdo gerado pelo último comando executado.

String getStderrData();

Retorna uma string que contém todo o conteúdo textual (mensagens de erro) gerado pelo programa ou comando, após ser executado. Essa função sempre retorna o conteúdo gerado pelo último comando executado.


Código-Fonte

CommandLine.java


import java.io.*;

public class CommandLine
{
// Needed to run commands in the command line
private Runtime runtime;
private Process process;

// Output of the executed command
private String stdinData;
private String stderrData;

// Constructor
public CommandLine()
{
runtime = Runtime.getRuntime();
process = null;
stdinData = "";
stderrData = "";
}

// Return executed command output data
public String getStdinData() { return stdinData; }
public String getStderrData() { return stderrData; }

// Execute a single command with all its arguments
public void execute( String command )
{
try { process = runtime.exec( command ); }
catch ( Exception ex ) { System.err.println( ex ); }

pullProcessData();
}

// Execute a single command in cmdArray,
// define environment settings in envp and
// set working directory to dir
public void execute( String[] cmdArray, String[] envp, File dir )
{
try { process = runtime.exec( cmdArray, envp, dir ); }
catch ( Exception ex ) { System.err.println( ex ); }

pullProcessData();
}

// Obtain all data sent by the process to stdout and stderr
private void pullProcessData()
{
try
{
StringBuffer stdinBuffer = new StringBuffer();
StringBuffer stderrBuffer = new StringBuffer();

InputStream stdinStream = process.getInputStream();
InputStream stderrStream = process.getErrorStream();

new InputStreamHandler( stdinBuffer, stdinStream );
new InputStreamHandler( stderrBuffer, stderrStream );

process.waitFor();

stdinData = stdinBuffer.toString();
stderrData = stderrBuffer.toString();
}
catch ( Exception ex )
{
System.err.println( ex );
}
}

// Private inner class used to handle input streams
private class InputStreamHandler extends Thread
{
private InputStream m_stream;
private StringBuffer m_captureBuffer;

InputStreamHandler( StringBuffer captureBuffer, InputStream stream )
{
m_stream = stream;
m_captureBuffer = captureBuffer;
start();
}

public void run()
{
try
{
int nextChar;
while ( ( nextChar = m_stream.read() ) != -1 )
m_captureBuffer.append( (char) nextChar );
}
catch( IOException ioex )
{
System.err.println( ioex );
}
}
}
}

Confira a seguir alguns exemplos de como utilizar a classe CommandLine.


Exemplos de Uso


O programa a seguir ilustra como utilizar a classe CommandLine para executar o comando DIR do Windows, que lista todos os diretórios e arquivos dentro do diretório atual.

A primeira linha do main() cria um objeto CommandLine. A seguir, chama-se o método execute() passando a string contendo o comando, neste caso "cmd /c dir", fazendo com que o sistema execute o programa CMD.EXE (que é o interpretador de comandos do Windows), com os parâmetros "/c" e "dir". O parâmetro "/c" serve para que o CMD.EXE encerre sozinho depois que executar. Por fim, "dir" é o comando do CMD.EXE que serve para listar diretórios e arquivos. As últimas duas linhas do main() escrevem na tela a saída do comando, usando getStdinData() para obter a saída de informações do comando e getStderrData() para obter a saída de erros do comando.

Copie e cole este código num arquivo chamado Main.java para compilar:

public class Main
{
public static void main( String args[] )
{
CommandLine cl = new CommandLine();

cl.execute( "cmd /c dir" );

System.out.print( cl.getStdinData() );
System.out.print( cl.getStderrData() );
}
}

O próximo exemplo de uso mostra como abrir o navegador Google Chrome e carregar o site do YouTube ao iniciar.

A primeira linha do main() cria um objeto CommandLine. A seguir, chama-se o método execute() passando como parâmetro o caminho completo do executável do Google Chrome no sistema, que no meu caso é "C:\Users\Fernando\AppData\Local\Google\Chrome\Application\chrome.exe", mas você pode trocar pelo caminho de instalação do seu navegador, sem esquecer que no Java é necessário usar barras invertidas duplas para não confundir com caracteres de escape. Repare também como no programa tem um espaço no final do caminho. Ele serve para separar o comando dos parâmetros. Isso é porque embaixo eu concateno o caminho com o parâmetro, que nada mais é do que o endereço do YouTube na Internet. Sem aquele espaço no final, o sistema pensa que o parâmetro faz parte do comando.

Copie e cole este código num arquivo chamado Main.java para compilar:

public class Main
{
public static void main( String args[] )
{
CommandLine cl = new CommandLine();

cl.execute
(
"C:\\Users\\Fernando\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe " +
"http://www.youtube.com/"
);
}
}

Os códigos-fonte neste artigo são todos Freeware e OpenSource, não tem nenhuma garantia, nem licença nem restrição, não me responsabilizo por quaisquer eventuais danos que possam causar a você ou a seu sistema operacional e você pode pegar eles e usar onde quizer, não precisa me pagar nada. Mas se quizer agradecer, é só deixar seu comentário logo abaixo :)


Caso não queira copiar, colar e compilar o código-fonte da CommandLine, baixe do 4Shared o arquivo-fonte e arquivo de classe, junto com um exemplo:




Espero que esta classe seja útil no seu projeto!
Quaisquer sugestões ou dúvidas, deixe seu recado.