Search code examples
perlstdin

assign specific value to perl STDIN


I'm performing a test and need to assign a specific value to STDIN within a perl code.

So instead of reading from stdin like this:

#myperl.pl
#!//usr/bin/env perl
#
while (<>) {
print;
}

# echo hello | ./myperl.pl
hello

I can just do this:

#myperl.pl
#!//usr/bin/env perl
#
<STDIN> = "hello";
while (<>) {
print;
}

./myperl.pl
# hello

Of course, the text hello can be multiple lines in a real test case scenario.


Solution

  • According to perldoc open:

    [...] you can open filehandles directly to Perl scalars via:

    open(my $fh, ">", \$variable) || ..   
    

    see also PerlIO::scalar. Further, according to perldoc perlop:

    The null filehandle <> is special: it can be used to emulate the behavior of sed and awk, and any other Unix filter program that takes a list of filenames, doing the same to each line of input from all of them. Input from <> comes either from standard input, or from each file listed on the command line. Here's how it works: the first time <> is evaluated, the @ARGV array is checked, and if it is empty, $ARGV[0] is set to "-" , which when opened gives you standard input. The @ARGV array is then processed as a list of filenames.

    So when you do while (<>) it will try to "open standard input" (provided you did not give command line arguments, i.e. @ARGV is empty). This open command is not affected by the current value of the variable STDIN, instead (I guess) it will simply do something like:

    open ARGV, '/dev/tty' or die "open: /dev/tty: $!";
    

    So it seems it is not possible to redefine the behavior of <> to read from a string by changing STDIN.

    But instead of using the null file handle <> in your loop, if you could use <STDIN> instead.. then redefining STDIN to a string file handle would work:

    use strict;
    use warnings;
    
    my $str = "hello\n";
    open my $fh, "<", \$str or die "Could not open string file handle: $!";
    {
        local *STDIN = $fh;
        while (<STDIN>) {
            print;
        }
    }
    close $fh;
    my $line = <STDIN>;
    print "Terminal input: ", $line;
    

    Edit:

    The following also seems to work:

    local *ARGV = $fh;
    while (<>) {
        print;
    }