0

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:

  • zit <(echo a;echo b) would result in ( -t STDIN ) being FALSE.

  • zit < <(echo a;echo b) would result in( -t STDIN ) being TRUE.

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.

1 Answer 1

3
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.

Sign up to request clarification or add additional context in comments.

4 Comments

Wow. Thanks. First matter: from what you're saying, it sounds like I should be testing ( -p STDIN ), which would make my code more readable and logical.
Thanks for the other comments about use strict and so forth.
@A.Ellett Well, what are you trying to achieve? Do you just want to know whether your STDIN is interactive (with a user typing into it) or not? If so, you want -t STDIN but with your expectations flipped around. :-)
I originally just wanted perl to rewrite the output of other commands into something more readable. That brought up a bag of worms about topics for which I don't have much of a firm grasp: namely pipes, redirection, etc. So, short answer is, no interactive user input for this particular application.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.