usados - runtime java ejemplo
¿Por qué Runtime.exec(String) funciona para algunos pero no para todos los comandos? (1)
¿Por qué fallan algunos comandos?
Esto sucede porque el comando pasado a
Runtime.exec(String)
no se ejecuta en un shell.
El shell realiza muchos servicios de soporte comunes para programas, y cuando el shell no está disponible para realizarlos, el comando fallará.
¿Cuándo fallan los comandos?
Un comando fallará siempre que dependa de las características de un shell. El shell hace muchas cosas comunes y útiles en las que normalmente no pensamos:
-
El caparazón se divide correctamente entre comillas y espacios.
Esto asegura que el nombre de archivo en
"My File.txt"
siga siendo un argumento único.Runtime.exec(String)
ingenuamente se divide en espacios y pasaría esto como dos nombres de archivo separados. Esto obviamente falla. -
El caparazón expande globos / comodines
Cuando ejecuta
ls *.doc
, el shell lo reescribe enls letter.doc notes.doc
.Runtime.exec(String)
no lo hace, solo los pasa como argumentos.ls
tiene idea de qué es*
, por lo que el comando falla. -
El shell gestiona tuberías y redirecciones.
Cuando ejecuta
ls mydir > output.txt
, el shell abre "output.txt" para la salida del comando y lo elimina de la línea de comando, dandols mydir
.Runtime.exec(String)
no lo hace. Simplemente los pasa como argumentos.ls
tiene idea de lo que significa, por lo que el comando falla. -
El shell expande variables y comandos
Cuando ejecuta
ls "$HOME"
ols "$(pwd)"
, el shell lo reescribe enls /home/myuser
.Runtime.exec(String)
no lo hace, solo los pasa como argumentos.ls
tiene idea de lo que significa$
, por lo que el comando falla.
¿Qué puedes hacer en su lugar?
Hay dos formas de ejecutar comandos arbitrariamente complejos:
Simple y descuidado: delegue a un shell.
Simplemente puede usar
Runtime.exec(String[])
(tenga en cuenta el parámetro de matriz) y pasar su comando directamente a un shell que puede hacer todo el trabajo pesado:
// Simple, sloppy fix. May have security and robustness implications
String myFile = "some filename.txt";
String myCommand = "cp -R ''" + myFile + "'' $HOME 2> errorlog";
Runtime.getRuntime().exec(new String[] { "bash", "-c", myCommand });
Seguro y robusto: asuma las responsabilidades del shell.
Esta no es una solución que se pueda aplicar mecánicamente, pero requiere una comprensión del modelo de ejecución de Unix, qué hacen los shells y cómo puede hacer lo mismo.
Sin embargo, puede obtener una solución sólida, segura y robusta quitando el caparazón de la imagen.
Esto es facilitado por
ProcessBuilder
.
El comando del ejemplo anterior que requiere que alguien maneje 1. comillas, 2. variables y 3. redirecciones, puede escribirse como:
String myFile = "some filename.txt";
ProcessBuilder builder = new ProcessBuilder(
"cp", "-R", myFile, // We handle word splitting
System.getenv("HOME")); // We handle variables
builder.redirectError( // We set up redirections
ProcessBuilder.Redirect.to(new File("errorlog")));
builder.start();
Cuando intento ejecutar
Runtime.exec(String)
, ciertos comandos funcionan, mientras que otros comandos se ejecutan pero fallan o hacen cosas diferentes que en mi terminal.
Aquí hay un caso de prueba autónomo que demuestra el efecto:
public class ExecTest {
static void exec(String cmd) throws Exception {
Process p = Runtime.getRuntime().exec(cmd);
int i;
while( (i=p.getInputStream().read()) != -1) {
System.out.write(i);
}
while( (i=p.getErrorStream().read()) != -1) {
System.err.write(i);
}
}
public static void main(String[] args) throws Exception {
System.out.print("Runtime.exec: ");
String cmd = new java.util.Scanner(System.in).nextLine();
exec(cmd);
}
}
El ejemplo funciona muy bien si reemplazo el comando con
echo hello world
, pero para otros comandos, especialmente aquellos que involucran nombres de archivos con espacios como aquí, obtengo errores a pesar de que el comando se está ejecutando claramente:
myshell$ javac ExecTest.java && java ExecTest
Runtime.exec: ls -l ''My File.txt''
ls: cannot access ''My: No such file or directory
ls: cannot access File.txt'': No such file or directory
Mientras tanto, copiar y pegar en mi shell:
myshell$ ls -l ''My File.txt''
-rw-r--r-- 1 me me 4 Aug 2 11:44 My File.txt
¿Por qué hay una diferencia? ¿Cuándo funciona y cuándo falla? ¿Cómo hago que funcione para todos los comandos?