The shell-free (but possibly less-portable) version:
#!perl
use strict;
use warnings;
use Fcntl;
open my $one, '-|', 'echo', 'string1' or die "$! opening first input";
fcntl $one, F_SETFD, (fcntl($one, F_GETFD, 0) & ~FD_CLOEXEC)
or die "$! keeping open first input";
open my $two, '-|', 'echo', 'string2' or die "$! opening second input";
fcntl $two, F_SETFD, (fcntl($two, F_GETFD, 0) & ~FD_CLOEXEC)
or die "$! keeping open second input";
open my $diff, '-|', 'diff', "/dev/fd/".fileno($one), "/dev/fd/".fileno($two)
or die "$! running diff";
while (<$diff>) {
print;
}
This is basically exactly what the shell does (open a subprocess for the first "echo" command, open a subprocess for the second "echo" command, allow their filehandles to be inherited by child processes, then open a subprocess to the "diff" command, with its arguments being special filenames that point to the opened filehandles). The only difference is that instead of allowing diff to print to the screen, we capture the output and print it ourselves — because I thought you might want to do something with the output rather than merely see it.
New version (no /bin/echo, no escaping, no deadlocks)
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl;
my $pid_one = open my $one, '-|';
die "$! opening first input" unless defined $pid_one;
if (!$pid_one) { # child
print "string1\n";
exit;
}
fcntl $one, F_SETFD, (fcntl($one, F_GETFD, 0) & ~FD_CLOEXEC)
or die "$! keeping open first input";
my $pid_two = open my $two, '-|';
die "$! opening second input" unless defined $pid_two;
if (!$pid_two) { # child
print "string2\n";
exit;
}
fcntl $two, F_SETFD, (fcntl($one, F_GETFD, 0) & ~FD_CLOEXEC)
or die "$! keeping open second input";
open my $diff, '-|', 'diff', "/dev/fd/".fileno($one), "/dev/fd/".fileno($two)
or die "$! running diff";
while (<$diff>) {
print;
}
string1
and string2
can be anything, including variables from the parent process or the output of other commands.