A.Ellett
A.Ellett

Reputation: 373

Redirected input via STDIN is not functioning as expected

I wrote some code where I thought I was testing for whether there was STDIN. But the code worked contrary to what I was expecting.

Here's the code I wrote, I've called it zit

#!/usr/bin/perl
use strict 'vars';

my @a = @ARGV ? @ARGV : "EMPTY";
printf "  command line arguments: \"%s\" as expected\n", @a;
if ( -t STDIN )
  {
    print "  ( -t STDIN ) returns TRUE\n";
  }
else
  {
    print "  ( -t STDIN ) returns FALSE\n";
    print "  But, I can iterate over <STDIN>!  Huh?? Behold:\n";
  }

Here's what I thought:

So, to try to figure out what might be going on, I modified the if-then code by adding a while loop (based upon my understanding, I put it where I thought it should create problems).

#!/usr/bin/perl
use strict 'vars';

my @a = @ARGV ? @ARGV : "EMPTY";
printf "  command line arguments: \"%s\" as expected\n", @a;

if ( -t STDIN )
  {
    print "  ( -t STDIN ) returns TRUE\n";
  }
else
  {
    print "  ( -t STDIN ) returns FALSE\n";
    print "  But, I can iterate over <STDIN>!  Huh?? Behold:\n";
    while ( <STDIN> )
      {
        print ">> $_";
      }
  }

Here's the output from this code

zit <(echo a;echo b) has the following output

  command line arguments: "/dev/fd/63" as expected
  ( -t STDIN ) returns TRUE

zit < <(echo a;echo b) has the following output

  command line arguments: "EMPTY" as expected
  ( -t STDIN ) returns FALSE
  But, I can iterate over <STDIN>!  Huh?? Behold:
>> a
>> b

I'm really confused by this. This is not behaving as I thought things should. If ( -t STDIN ) is false, why does the while loop work?

Could someone explain what's happening here?

I've re-edited this post from before to be a bit less confusing.

Upvotes: 0

Views: 51

Answers (1)

melpomene
melpomene

Reputation: 85757

use strict 'vars';

Why are you doing this? You should really just use strict; and enable all three strictures, not just vars. You should also use warnings;.


If you check perldoc -f -t, you'll see that the documentation says:

-t Filehandle is opened to a tty.

In other words, it's not testing whether there is a STDIN (there always is a STDIN (unless your parent process was naughty and closed it before starting you)), but whether STDIN refers to a terminal (the abbreviation "tty" originally referred to a teletype, but the name kind of stuck when they were replaced by text terminals and later terminal emulators such as xterm). In C terms, it corresponds to a call to isatty.

In your first example, you have zit <(echo a;echo b). The <( ... ) notation makes bash run the specified command with its STDOUT redirected to a pipe. It then substitutes <( ... ) by a filename that refers to (the read end of) the pipe.

Specifically, it runs zit /dev/fd/63 (where file descriptor 63 refers to the read end of a pipe whose write end is fed by echo a; echo b). Nothing happens to STDIN, so zit inherits its standard streams from the shell, so STDIN refers to a terminal, which is why -t STDIN returns true.

In your second example, you have zit < <(echo a;echo b). This is very similar, but now the resulting command is zit < /dev/fd/63. The < FILE notation tells the shell to open the specified file for reading and redirect STDIN to it.

Thus it runs zit (without any command line arguments) with STDIN connected to a pipe (whose other end is fed by echo a; echo b). This is effectively the same as ( echo a; echo b ) | zit. Here zit's STDIN refers to a pipe, not a terminal, so -t STDIN returns false. You can still read from it, of course: Most things you can read from are not terminals, such as plain files, pipes, or sockets.

Upvotes: 3

Related Questions