2009-12-15 16 views
6

Necesito ejecutar una cadena a través de un programa Java y luego recuperar la salida. El programa Java acepta la cadena a través de la entrada estándar. Las siguientes obras:¿Cómo puedo canalizar la entrada en un comando de Java desde Perl?

my $output = `echo $string | java -jar java_program.jar`; 

hay un problema: $string podría ser casi cualquier cosa. ¿Alguna idea sobre una buena solución a este problema?

Respuesta

6

sugiero que mire IPC::Run3 módulo. Utiliza una interfaz muy simple y permite obtener STDERR y STDOUT. Aquí es pequeño ejemplo:

use IPC::Run3; 
## store command output here 
my ($cmd_out, $cmd_err); 
my $cmd_input = "put your input string here"; 
run3([ 'java', '-jar', 'java_program.jar'], \$cmd_input, \$cmd_out, \$cmd_err); 
print "command output [$cmd_out] error [$cmd_err]\n"; 

Ver IPC::Run3comparation with other modules.

6

Si puede usar los módulos de CPAN (y supongo que la mayoría de la gente puede), mire Ivan's answer usando IPC::Run3. Debe manejar todo lo que necesita.

Si no puede usar módulos, he aquí cómo hacer las cosas de la manera normal.

Se puede utilizar una tubería para hacer su entrada, y va a evitar todos esos problemas citando la línea de comandos:

open PIPE, "| java -jar java_program.jar"; 
print PIPE "$string"; 
close(PIPE); 

Parece que realmente necesita la salida de la orden, sin embargo. Usted podría abrir dos tuberías con algo así como IPC::Open2 (hacia y desde el proceso de Java) pero corre el riesgo de ponerse en un punto muerto tratando de hacer frente a ambas tuberías al mismo tiempo.

Usted puede evitar que al tener salida de Java en un archivo, entonces la lectura de ese archivo:

open PIPE, "| java -jar java_program.jar > output.txt"; 
print PIPE "$string"; 
close(PIPE); 

open OUTPUT, "output.txt"; 
while (my $line = <OUTPUT>) { 
    # do something with $line 
} 
close(OUTPUT); 

La otra opción es hacer las cosas al revés. Ponga $ cadena en un archivo temporal, y luego usarlo como entrada a java:

open INPUT, "input.txt"; 
print INPUT "$string"; 
close(INPUT); 

open OUTPUT, "java -jar java_program.jar < input.txt |"; 
while (my $line = <OUTPUT>) { 
    # do something with the output 
} 
close(OUTPUT); 

Tenga en cuenta que esta no es la mejor manera de hacerlo archivos temporales; Acabo de utilizar output.txt y input.txt por simplicidad. Mire el File::Temp docs para varias maneras más limpias de crear archivos temporales más limpiamente.

+0

Eso se ve genial, pero ¿cómo obtengo el resultado del comando? – spudly

+1

@spudly, no se puede con esa recomendación. Debería usar open2(), comprobar perlc perlipc para "comunicación bidireccional" – jsoverson

+0

Muchos usos de open2 o open3 en sistemas Windows se ven fatalmente obstaculizados por el hecho de que solo puede 'seleccionar' contra un socket en Win32. Si no está en Windows o el bloqueo está bien, entonces open2 o open3 pueden hacer el truco para usted. – daotoad

1

El módulo integrado IPC :: Open2 proporciona una función para manejar tuberías bidireccionales sin un archivo externo.

2

¿Has mirado en IPC::Run?

sintaxis similar a esto podría ser lo que buscas:

use IPC::Run qw(run); 
my $input = $string; 
my ($out, $err); 
run ["java -jar java_program.jar"], \$input, \$out, \$err; 
2

Crea una tubería como lo haría tu caparazón.

Aquí está nuestra cadena de miedo:

my $str = "foo * ~ bar \0 baz *"; 

Vamos a desarrollar nuestro canal hacia atrás, por lo que primero nos reunimos la salida del programa Java:

my $pid1 = open my $fh1, "-|"; 
die "$0: fork: $!" unless defined $pid1; 

if ($pid1) { 
    # grab output from Java program 
    while (<$fh1>) { 
    chomp; 
    my @c = unpack "C*" => $_; 
    print "$_\n => @c\n"; 
    } 
} 

Nota el especial "-|" argumento para Perl de open operador.

Si abre un tubo en el comando '-', es decir, ya sea '|-' o '-|' con 2-argumentos (o 1-argumento) forma de open(), entonces hay una implícita fork hecho, y el valor de retorno de open es el pid del hijo dentro del proceso principal, y 0 dentro del proceso hijo ... El manejador de archivo se comporta normalmente para el elemento primario, pero la E/S de ese identificador de archivo se canaliza desde/al STDOUT/STDIN del proceso secundario.

El unpack está ahí para echar un vistazo en el contenido de los datos leídos de la tubería.

En su programa, tendrá que ejecutar el programa Java, pero el siguiente código utiliza un facsímil razonable:

else { 
    my $pid2 = open my $fh2, "-|"; 
    die "$0: fork: $!" unless defined $pid2; 

    if ($pid2) { 
    $| = 1; 
    open STDIN, "<&=" . fileno($fh2) 
     or die "$0: dup: $!"; 

    # exec "java", "-jar", "java_program.jar"; 

    # simulate Java program 
    exec "perl", "-pe", q(
     BEGIN { $" = "][" } 
     my @a = split " ", scalar reverse $_; 
     $_ = "[@a]\n"; 
    ); 
    die "$0: exec failed"; 
    } 

Por último, el humilde nieto simplemente imprime la cadena de miedo (que llega a la norma entrada del programa Java) y salidas. Al establecer $| en un valor verdadero, se limpia el identificador de archivo seleccionado actualmente y se lo coloca en modo sin búfer.

else { 
    print $str; 
    $| = 1; 
    exit 0; 
    } 
} 

Su salida:

 
$ ./try 
[*][zab][][rab][~][*][oof] 
    => 91 42 93 91 122 97 98 93 91 0 93 91 114 97 98 93 91 126 93 91 42 93 91 111 111 102 93 

Tenga en cuenta que la NUL sobrevive el viaje.

Cuestiones relacionadas