I need to run a string through a Java program and then retrieve the output. The Java program accepts the string through standard input. The following works:
my $output = `echo $string | java -jar java_program.jar`;
There is one problem: $string
could be just about anything. Any thoughts on a good solution to this problem?
I suggest you to look at IPC::Run3
module. It uses very simple interface and allow to get STDERR
and STDOUT
. Here is small example:
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";
See IPC::Run3
comparation with other modules.
If you can use CPAN modules (and I'm assuming most people can), look at Ivan's answer on using IPC::Run3. It should handle everything you need.
If you can't use modules, here's how to do things the plain vanilla way.
You can use a pipe to do your input, and it will avoid all those command line quoting issues:
open PIPE, "| java -jar java_program.jar";
print PIPE "$string";
close(PIPE);
It looks like you actually need the output of the command, though. You could open two pipes with something like IPC::Open2 (to and from the java process) but you risk putting yourself in deadlock trying to deal with both pipes at the same time.
You can avoid that by having java output to a file, then reading from that file:
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);
The other option is to do things the other way around. Put $string in a temporary file, then use it as input to 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);
Note that this isn't the greatest way to do temporary files; I've just used output.txt
and input.txt
for simplicity. Look at the File::Temp docs for various cleaner ways to create temporary files more cleanly.
Have you looked into IPC::Run
?
Syntax similar to this might be what you are looking for:
use IPC::Run qw( run );
my $input = $string;
my ($out, $err);
run ["java -jar java_program.jar"], \$input, \$out, \$err;
Create a pipeline just like your shell would.
Here's our scary string:
my $str = "foo * ~ bar \0 baz *";
We'll build our pipeline backwards, so first we gather the output from the Java program:
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";
}
}
Note the special "-|"
argument to Perl's open
operator.
If you open a pipe on the command '-'
, i.e., either '|-'
or '-|'
with 2-arguments (or 1-argument) form of open()
, then there is an implicit fork
done, and the return value of open
is the pid of the child within the parent process, and 0
within the child process … The filehandle behaves normally for the parent, but i/o to that filehandle is piped from/to the STDOUT
/STDIN
of the child process.
The unpack
is there to peek into the contents of the data read from the pipe.
In your program, you'll want to run the Java program, but the code below uses a reasonable facsimile:
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";
}
Finally, the humble grandchild simply prints the scary string (which arrives on the standard input of the Java program) and exits. Setting $|
to a true value flushes the currently selected filehandle and puts it in unbuffered mode.
else {
print $str;
$| = 1;
exit 0;
}
}
Its output:
$ ./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
Note that the NUL survives the trip.
The builtin IPC::Open2 module provides a function to handle bidirectional-piping without an external file.