jump to navigation

Ejecutar Shell Scripts desde Java Marzo 14, 2007

Posted by superpiwi in Java, Oracle, Programacion, Unix.
trackback

Y ya que hemos hablado en el Post anterior de Shell Scripts, porque no ejecutarlos directamente desde codigo fuente con Java. Antes de nada recomiendo una lectura:

When Runtime.exec() won’t

En este caso he creado una clase propia “Script.java” y he adaptado la del ejemplo que aparece en el articulo anterior.

Esta clase Script nos permite ejecutar comandos y shell scripts empleando el interprete de comandos.

Por ejemplo el siguiente codigo:


Script sc = new Script();
sc.setVerbose(true);
String CONTENT = "sqlplus2\nls -l\nps\necho \"End\\nFin\"";
String OUTPUT = sc.executeScript(CONTENT,
"/home/jose/Desktop/MiScript.sh", true);
System.out.println(OUTPUT);
String ERROR = sc.getError();
System.out.println("**ERROR**" + ERROR);

Como observais estoy lanzando la ejecucion de estos tres comandos:


sqlplus2 (que no existe y dara error)
ls -l
ps
y un echo End salto de linea Fin

Al ejecutar este codigo desde java nos devolveria lo siguiente:


ERROR> /home/jose/Desktop/MiScript.sh: 1: sqlplus2: not found
OUTPUT> total 44
OUTPUT> drwxr-xr-x 3 jose jose 4096 2007-01-11 09:18 ant
OUTPUT> drwxr-xr-x 6 jose jose 4096 2007-02-01 17:34 build
OUTPUT> -rw-r--r-- 1 jose jose 12953 2007-02-01 17:34 build.xml
OUTPUT> drwxr-xr-x 2 jose jose 4096 2007-01-11 09:17 CVS
OUTPUT> drwxr-xr-x 7 jose jose 4096 2007-01-11 09:17 Documentacion
OUTPUT> drwxr-xr-x 7 jose jose 4096 2007-01-11 09:17 JavaSource
OUTPUT> -rwxr-xr-x 1 jose jose 111 2007-03-14 17:13 pepe.sh
OUTPUT> drwxr-xr-x 11 jose jose 4096 2007-02-20 15:52 WebContent
OUTPUT> PID TTY TIME CMD
OUTPUT> 4887 ? 00:00:00 gconfd-2
OUTPUT> 11652 ? 00:00:00 x-session-manag
OUTPUT> 11665 ? 00:00:02 Scribe.exe <defunct>
OUTPUT> 11693 ? 00:00:00 ssh-agent
OUTPUT> 11696 ? 00:00:00 dbus-daemon
OUTPUT> 11697 ? 00:00:00 dbus-launch
OUTPUT> 11700 ? 00:00:00 gnome-keyring-d
OUTPUT> 11703 ? 00:00:03 gnome-settings-
OUTPUT> 11727 ? 00:00:27 metacity
OUTPUT> 11732 ? 00:00:23 gnome-panel
OUTPUT> 11734 ? 00:00:15 nautilus
OUTPUT> 11738 ? 00:00:00 bonobo-activati
OUTPUT> 11741 ? 00:00:00 gnome-vfs-daemo
OUTPUT> 11747 ? 00:00:00 update-notifier
OUTPUT> 11749 ? 00:00:00 gnome-volume-ma
OUTPUT> 11751 ? 00:00:00 evolution-alarm
OUTPUT> 11759 ? 00:00:10 trackerd
OUTPUT> 11762 ? 00:00:15 gnome-cups-icon
OUTPUT> 11774 ? 00:00:02 python
OUTPUT> 11776 ? 00:00:00 trashapplet
OUTPUT> 11779 ? 00:00:00 evolution-data-
OUTPUT> 11803 ? 00:00:00 evolution-excha
OUTPUT> 11833 ? 00:00:00 mapping-daemon
OUTPUT> 11834 ? 00:00:00 gnome-power-man
OUTPUT> 11843 ? 00:00:00 mixer_applet2
OUTPUT> 11885 ? 00:00:02 notification-da
OUTPUT> 11912 ? 00:00:05 gnome-screensav
OUTPUT> 12025 ? 00:01:49 skype
OUTPUT> 12094 ? 00:04:30 firefox-bin
OUTPUT> 12270 ? 00:00:00 eclipse
OUTPUT> 12276 ? 00:03:44 java
OUTPUT> 14360 ? 00:00:03 gnome-terminal
OUTPUT> 14369 ? 00:00:00 gnome-pty-helpe
OUTPUT> 14597 ? 00:01:59 wineserver
OUTPUT> 14599 ? 00:00:00 explorer.exe
OUTPUT> 17598 ? 00:00:00 java
OUTPUT> 17617 ? 00:00:00 sh
OUTPUT> 17618 ? 00:00:00 sh
OUTPUT> 17622 ? 00:00:00 ps
OUTPUT> End
OUTPUT> Fin
total 44
drwxr-xr-x 3 jose jose 4096 2007-01-11 09:18 ant
drwxr-xr-x 6 jose jose 4096 2007-02-01 17:34 build
-rw-r--r-- 1 jose jose 12953 2007-02-01 17:34 build.xml
drwxr-xr-x 2 jose jose 4096 2007-01-11 09:17 CVS
drwxr-xr-x 7 jose jose 4096 2007-01-11 09:17 Documentacion
drwxr-xr-x 7 jose jose 4096 2007-01-11 09:17 JavaSource
-rwxr-xr-x 1 jose jose 111 2007-03-14 17:13 pepe.sh
drwxr-xr-x 11 jose jose 4096 2007-02-20 15:52 WebContent
PID TTY TIME CMD
4887 ? 00:00:00 gconfd-2
11652 ? 00:00:00 x-session-manag
11665 ? 00:00:02 Scribe.exe <defunct>
11693 ? 00:00:00 ssh-agent
11696 ? 00:00:00 dbus-daemon
11697 ? 00:00:00 dbus-launch
11700 ? 00:00:00 gnome-keyring-d
11703 ? 00:00:03 gnome-settings-
11727 ? 00:00:27 metacity
11732 ? 00:00:23 gnome-panel
11734 ? 00:00:15 nautilus
11738 ? 00:00:00 bonobo-activati
11741 ? 00:00:00 gnome-vfs-daemo
11747 ? 00:00:00 update-notifier
11749 ? 00:00:00 gnome-volume-ma
11751 ? 00:00:00 evolution-alarm
11759 ? 00:00:10 trackerd
11762 ? 00:00:15 gnome-cups-icon
11774 ? 00:00:02 python
11776 ? 00:00:00 trashapplet
11779 ? 00:00:00 evolution-data-
11803 ? 00:00:00 evolution-excha
11833 ? 00:00:00 mapping-daemon
11834 ? 00:00:00 gnome-power-man
11843 ? 00:00:00 mixer_applet2
11885 ? 00:00:02 notification-da
11912 ? 00:00:05 gnome-screensav
12025 ? 00:01:49 skype
12094 ? 00:04:30 firefox-bin
12270 ? 00:00:00 eclipse
12276 ? 00:03:44 java
14360 ? 00:00:03 gnome-terminal
14369 ? 00:00:00 gnome-pty-helpe
14597 ? 00:01:59 wineserver
14599 ? 00:00:00 explorer.exe
17598 ? 00:00:00 java
17617 ? 00:00:00 sh
17618 ? 00:00:00 sh
17622 ? 00:00:00 ps
End
**ERROR**/home/jose/Desktop/MiScript.sh: 1: sqlplus2: not found

Sencillo y potente!!!

Tambien permite ejecutar shell scripts creandolos dinamicamente. esto por ejemplo nos sirve para lanzar scripts que solo contienen sentencias SQL. Por ejemplo el siguiente caso:


Script sql = new Script();
sql.setVerbose(false);
sql.setAddSHELLHEADER(true);
sql.setSHELLHEADER("#!/bin/sh");
//sql.setRUTA("/home/jose/Desktop");
CONTENT = "SELECT sysdate FROM DUAL;\nSELECT sysdate FECHA_HOY FROM DUAL;";
OUTPUT = sql.executeSQLScript(CONTENT, "EVO", "EVO", "EVODES",
null, "/home/jose/Desktop/MiSQL.sh", false);
System.out.println(OUTPUT);
ERROR = sql.getError();
System.out.println("**ERROR**" + ERROR);

Que nos devolveria:


SQL*Plus: Release 10.2.0.3.0 - Production on Wed Mar 14 17:38:06 2007
Copyright (c) 1982, 2006, Oracle. All Rights Reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
With the Partitioning option
JServer Release 9.2.0.8.0 - Production
SQL>
SYSDATE
---------
14-MAR-07
SQL>
FECHA_HOY
---------
14-MAR-07
SQL> Disconnected from Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
With the Partitioning option
JServer Release 9.2.0.8.0 - Production
**ERROR**

Aqui el truco es generar dinamicamente un fichero .sh con el contenido que se quiere ejecutar y luego lanzar el interprete de comandos para ejecutar ese fichero creado dinamicamente.

Pero mejor veis las clases y ya trasteais con ellas. Aqui las dejo.

StreamGlobber.java


/**
* StramGobbler.java
*
* Para capturar la salida de la ejecucion del comando.
*
* @modified clase original de javaworld modificada por jdelgado
* @version 0.0.0.2 - 13 Marzo 2007
* @sinde JDK 1.5 - Eclipse 3.2
*/
package comun.util.unix;
import java.io.*;
class StreamGobbler extends Thread {
// Flujo de entrada
InputStream is;
// Tipo de Flujo: generalmente output o error
String type;
// Flujo de salida
OutputStream os;
// Variable para mostrar por la salida estandard
boolean debug = false;
// para guardar la salida generada
String output = "";
// ----
/**
* Constructor
*
* @param is
* InputStream
* @param type
* tipo de flujo (OUTPUT o ERROR)
* @param debug
* indica si se debe mostrar o no la salida por la salida
* estandard
*/
StreamGobbler(InputStream is, String type, boolean debug) {
this(is, type, null, debug);
}
// ----
/**
* Constructor
*
* @param is
* InputStram
* @param type
* tipo de flujo (OUTPUT o ERROR)
* @param redirect
* OutputStream donde redireccionar la salida
* @param debug
* indica si se debe mostrar o no la salida por la salida
* estandard
*/
StreamGobbler(InputStream is, String type, OutputStream redirect,
boolean debug) {
this.is = is;
this.type = type;
this.os = redirect;
this.debug = debug;
}
// ----
/**
* Ejecutar el hilo
*/
public void run() {
try {
PrintWriter pw = null;
if (os != null)
pw = new PrintWriter(os);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
// si hay fichero lo imprime a fichero
if (pw != null)
pw.println(line);
if (debug)
System.out.println(type + "> " + line);
output = output + line + "\r\n";
}
if (pw != null)
pw.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
// ------
/**
* Recuperar el flujo de datos generado
*
* @return un String con el output
*/
public String getOutput() {
return output;
}
// ------
}
// end of class StreamGobbler.java

y la clase Script.java:


/**
* Script.java
*
* Clase para ejecutar scripts unix o SQL localmente
*
* @author jdelgado
* @version 0.0.0.1 - 13 Marzo 2007
* @since JDK 1.5 - Eclipse 3.2
*
*/
package comun.util.unix;
import java.io.File;
import java.io.FileOutputStream;
public class Script {
// contenido a ejecutar
private String content = null;
// interprete de comandos: Si usas Windows adaptalo para usar CMD
private String SHELL = "/bin/sh";
// resultado de la ejecucion
private String output = null;
// posibles errores en la ejecucion
private String error = null;
// imprimir o no traza en los threads
private Boolean verbose = false;
// path por defecto para ficheros temporales
private String RUTA = "/tmp";
// cabecera para shell scripts
private String SHELLHEADER = "#!/bin/sh";
// indica si hay que añadir el interprete a los shell scripts que se generen
private boolean addSHELLHEADER = false;
// caracter de retorno
private String RETORNO = "\n";
// ----
/**
* Constructor. crea una nueva instancia de la clase
*/
public Script() {
}
// ----
/**
* Constructor. crea una nueva instancia de la clase
*
* @param content
* contenido del script a ejecutar
*/
public Script(String content) {
this.content = content;
}
// ----
/**
* Ejecuta el script
*
* @throws Exception
* excepcion levantada en caso de error
*/
public String executeCommand() throws Exception {
output = null;
error = null;
StreamGobbler errorGobbler = null;
StreamGobbler outputGobbler = null;
try {
if (content == null)
throw new Exception("command is null");
// String ruta="/home/jose/Desktop/temporal.txt";
// FileOutputStream fos = new FileOutputStream(ruta);
Runtime rt = Runtime.getRuntime();
// Process proc = rt.exec(new String[]{"/bin/sh", "-c", "cd
// /home/jose/Desktop\n./SQLPLUS.sh\ncal\\nps -ef"});
String[] data = new String[3];
data[0] = this.SHELL;
data[1] = "-c";
data[2] = this.content;
Process proc = rt.exec(data);
// any error message?
errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR",
this.verbose);
// any output?
outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT",
null, this.verbose);
// StreamGobbler(proc.getInputStream(), "OUTPUT", fos);
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
} catch (Throwable t) {
t.printStackTrace();
} finally {
String out = outputGobbler.getOutput();
this.output = out;
output = out;
out = errorGobbler.getOutput();
this.error = out;
error = out;
}
return output;
}
// ----
/**
* Recupera la salida de la ejecucion del comando
* @return un string con el resultado de la ejecucion
*/
public String getOutput() {
return output;
}
// ----
/**
* Recupera el error de la ejecucion del comando
* @return un string con el error de la ejecucion
*/
public String getError() {
return error;
}
// ----
/**
* Ejecuta un Script creando un fichero .sh con el contenido y ejecutandolo
*
* @param content
* contenido del script
* @param pathname
* el nombre del fichero que se crea si es null lo crea en /tmp y
* de la forma /tmp/timestamp.sh
* @param delete
* indica si se debe borrar o no el fichero temporal tras su
* ejecucion
* @result el resultado de la ejecucion del Script
* @throws Exception
* Excepcion levantada en caso de error
*/
public String executeScript(String content, String pathname, boolean delete)
throws Exception {
File f = null;
FileOutputStream fout = null;
try {
// crear fichero .sh
if (pathname == null) {
long timestamp = System.currentTimeMillis();
pathname = RUTA + File.separator + timestamp + ".sh";
}
if (this.addSHELLHEADER)
{
content = this.SHELLHEADER+this.RETORNO+content;
}
traza("executeScript","Generando fichero [" + pathname + "]...");
f = new File(pathname);
fout = new FileOutputStream(f);
fout.write(content.getBytes());
if (fout != null)
fout.close();
traza("executeScript","Fichero generado");
// darle permisos de ejecucion
traza("executeScript","Asignando permisos...");
Script permisos = new Script("chmod +x " + pathname);
permisos.setVerbose(this.verbose);
permisos.executeCommand();
String error = permisos.getError();
if (error != null) {
if (error.length() > 0)
throw new Exception("Permission denied : " + error);
}
traza("executeScript","Permisos asignados");
// ejecutar fichero .sh
traza("executeScript","Ejecutando script...");
Script ejecucion = new Script(pathname);
ejecucion.setVerbose(this.verbose);
String result = ejecucion.executeCommand();
error = ejecucion.getError();
this.error = error;
this.output = result;
traza("executeScript","Script ejecutado");
// eliminar fichero .sh
if (delete) {
if (f != null) {
if (f.exists())
f.delete();
}
traza("executeScript","fichero eliminado");
}
return result;
} catch (Exception e) {
throw e;
}
}
// ----
/**
* Para probar el correcto funcionamiento de la clase
* @param args argumentos de entrada
*/
public static void main(String[] args) {
try {
// sqlplus directamente no mejor ejecutar sh shellscript.sh que tenga el
// contenido
// Script sc = new Script("ls -l\ncal2\nps -ef | grep
// java\nsqlplus");
// sc.setDebug(false);
// String output = sc.executeCommand();
// System.out.println("OUT:-------------------\r\n"+output);
// String error = sc.getError();
// System.out.println("ERROR:-----------------\r\n"+error);
// output = sc.getOutput();
// System.out.println("output:\r\n"+output);
// System.out.println("***ERROR***");
// String err = sc.getError();
// System.out.println("error:"+err);
Script sc = new Script();
sc.setVerbose(true);
String CONTENT = "sqlplus2\nls -l\nps\necho \"End\\nFin\"";
String OUTPUT = sc.executeScript(CONTENT,
"/home/jose/Desktop/MiScript.sh", true);
System.out.println(OUTPUT);
String ERROR = sc.getError();
System.out.println("**ERROR**" + ERROR);
Script sql = new Script();
sql.setVerbose(false);
sql.setAddSHELLHEADER(true);
sql.setSHELLHEADER("#!/bin/sh");
//sql.setRUTA("/home/jose/Desktop");
CONTENT = "SELECT sysdate FROM DUAL;\nSELECT sysdate FECHA_HOY FROM DUAL;";
OUTPUT = sql.executeSQLScript(CONTENT, "EVO", "EVO", "EVODES",
null, "/home/jose/Desktop/MiSQL.sh", false);
System.out.println(OUTPUT);
ERROR = sql.getError();
System.out.println("**ERROR**" + ERROR);
} catch (Exception e) {
e.printStackTrace();
}
}
// ----
/**
* Completa un script de SQL para poder ejecutarlo con sqlplus desde un
* shell script
*
* @param sqlcontent
* sentencias SQL
* @param user
* usuario de base de datos
* @param password
* password de la base de datos (TODO: guardar encriptada)
* @param sid
* sid de la base de datos
* @param redirect
* si se indica es el fichero donde se redirecciona la salida del
* sqlplus
* @return un string con el sqlplus
*/
public String prepareSQLScript(String sqlcontent, String user,
String password, String sid, String redirect) {
if (redirect == null)
redirect = "";
else
redirect = "> " + redirect;
String INICIO_SQL = "sqlplus " + user + "/" + password + "@" + sid
+ " << EOF" + " " + redirect;
String FIN_SQL = "EOF";
String sql = sqlcontent;
sql = INICIO_SQL + RETORNO + sql + RETORNO + "exit" + RETORNO + FIN_SQL;
return sql;
}
// ----
/**
* Ejecuta un Script SQL mediante sqlplus
*
* @param sqlcontent
* las sentencias SQL
* @param user
* usuario de la base de datos
* @param password
* la password de la base de datos
* @param redirect
* si se indica es el fichero donde se redirecciona la salida del
* sqlplus
* @param pathname
* nombre del shell script que se crea
* @param delete
* indica si se debe borrar o no el shell script tras la
* ejecucion
* @return el resultado de la ejecucion del script
*/
public String executeSQLScript(String sqlcontent, String user,
String password, String sid, String redirect, String pathname,
boolean delete) throws Exception {
String SQL_CONTENT = prepareSQLScript(sqlcontent, user, password, sid,
redirect);
return executeScript(SQL_CONTENT, pathname, delete);
}
// ----
/**
* Recupera el valor del flag
* @return el valor del flag
*/
public Boolean getVerbose() {
return verbose;
}
// ----
/**
* Fija el valor del flag para mostrar por la consola estandard la ejecucion
* @param debug el valor del flag
*/
public void setVerbose(Boolean debug) {
this.verbose = debug;
}
// ----
/**
* Para imprimir un mensaje de traza
*
* @param metodo
* nombre del metodo
* @param mensaje
* nombre de la clase
*/
public void traza(String metodo, String mensaje) {
// TODO: reemplazar aqui para emplear LOG4J
// System.out.println(mensaje);
}
// ----
public boolean isAddSHELLHEADER() {
return addSHELLHEADER;
}
public void setAddSHELLHEADER(boolean addSHELLHEADER) {
this.addSHELLHEADER = addSHELLHEADER;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRETORNO() {
return RETORNO;
}
public void setRETORNO(String retorno) {
RETORNO = retorno;
}
public String getRUTA() {
return RUTA;
}
public void setRUTA(String ruta) {
RUTA = ruta;
}
public String getSHELL() {
return SHELL;
}
public void setSHELL(String shell) {
SHELL = shell;
}
public String getSHELLHEADER() {
return SHELLHEADER;
}
public void setSHELLHEADER(String shellheader) {
SHELLHEADER = shellheader;
}
public void setError(String error) {
this.error = error;
}
public void setOutput(String output) {
this.output = output;
}
}
// end Script.java

Pueden ser de utilidad cuando queremos automatizar tareas (ejecucion automatica de shell scripts) y no queremos meternos en mucha faena escribiendolos en ANT. Pero bueno, de esto ya hablaremos otro dia…

shell.jpg

Comentarios»

1. max - Septiembre 12, 2007

Hola, Gracias por publicar esto esta muy bueno, aunque no es exactamente lo que necesito si me sirve para a partir de esto hacer lo que quiero. y eso es ejecutar scripts desde java pero usando “sudo” suponiendo que el usuario no esta con cuenta de administrador, entonces el sistema le solicitara la clave y necesito lograr que el usuario ingrese su contraseña y java pase esa contraseña al sistema para poder ejecutar el script.

Saludos.

2. javier - Octubre 16, 2007

Consulta, Donde guardas la clase que generaste, yo estoy trabajando con un linux y llamo a la case desde un jsp de esta manera:
Pero no lo encuentra, porque creo estoy guardando en un direcotrio incorrecto la clase.

3. superpiwi - Octubre 19, 2007

Dime el error que te da e intento ayudarte mejor.
De todas formas estoy trabajando en crear un framework de ejecucion y control de procesos. va a ser la leche xD
cuando lo tenga terminado lo compartire para la comunidad.

4. superpiwi - Octubre 19, 2007
5. Andrey - Agosto 11, 2009

Pero como hago para iniciar un Telnet, en windos se hace cmd /c start telnet xxx.xxx.xxx.xxx
pero en linux como?
no me funciona lo del start.
hay otro metodo o q?