If you are on a Unix system, you can redirect STDIN to pull input from a file. You can also open the file a second time for appending to obtain an independent file descriptor. You can write into this descriptor to inject input into STDIN. The OS will keep track of the input and output file positions separately, effectively turning the file into a fifo queue with a history.
Using this approach, you can inject on the fly, interleave injections and reads, and also simulate EOF input conditions.
The following code, which should work back to Perl 5.6, shows one way to do it.
#! perl
use warnings;
use strict;
use File::Temp qw( tempfile );
# this function encapsulates the technique.
# it returns a function that can be used to
# inject input into the targeted file handle
sub inject_input {
my $target_fh = shift;
my ($temp_fh, $temp_fn) = tempfile();
my $temp_fd = fileno $temp_fh;
local *SAVED;
local *TARGET = $target_fh;
local *INJECT;
open SAVED, "<&TARGET" or die "can't remember target";
open TARGET, "<&=$temp_fd" or die "can't redirect target";
open INJECT, "+>>$temp_fn" or die "can't open injector";
unlink $temp_fn; # we don't need the directory entry any more
select((select(INJECT), $| = 1)[0]);
my $saved_fh = *SAVED;
my $inject_fh = *INJECT;
return sub {
if (@_) {
print $inject_fh @_;
}
else {
seek $inject_fh, 0, 0 or die "can't seek"; # rewind
my $injected_output = do { local $/; <$inject_fh> };
close $temp_fh or die "can't close temp file handle";
local (*SAVED, *TARGET, *INJECT)
= ($saved_fh, $target_fh, $inject_fh);
open TARGET, "<&SAVED" or die "can't restore target";
close SAVED or die "can't close SAVED";
close INJECT or die "can't close INJECT";
return $injected_output;
}
}
}
Here is an example of how to use the function:
# let us inject input into STDIN
my $injector = inject_input(*STDIN);
# the resulting injector takes a string to inject
$injector->("1234\n"); # inject 1234
print "r: ", scalar <STDIN>; # read 1234 back in
# now we can inject 5678, a, and b
# we can read them back in via a loop
$injector->("5678\n"); # inject 5678
$injector->("a\n", "b\n"); # inject a and b
while (<>) {
print "R: $_";
}
# after the read loop reaches the EOF, we
# can still inject more input and read it
$injector->("we're outta here\n");
print "r: ", <>;
# when we are done, we call the injector with
# no arguments; this will close down the injection
# apparatus, restore STDIN to its pre-injection
# condition, and return a copy of what was injected
# for flight-recording purposes
print "\n\nwe injected:\n\n", $injector->();
# now STDIN is restored
print "\n\nEnter text by hand:\n";
while (<>) {
print "You entered: $_";
}
# actual output follows:
__END__
r: 1234
R: 5678
R: a
R: b
r: we're outta here
we injected:
1234
5678
a
b
we're outta here
Enter text by hand:
blah
You entered: blah
blah
You entered: blah
blah
You entered: blah
I hope this helps.
Cheers,
Tom