perlstdin

Testing -t STDIN vs -t <STDIN>


I'm writing an executable perl script, let's call it SCRIPT, that takes the output from another command executed from the command line and does something with that output.

For example

$ diff fileA fileB | SCRIPT

But if I call SCRIPT without putting it after a pipe I want to signal an error.

The following MWE works correctly, but I'm not sure I understand why.

#!/usr/bin/perl
use strict;
use warnings;

if ( -t STDIN )
  {
    print "NOTHING PIPED IN\n";
  }
else
  {
    while ( my $line = <STDIN> )
      {
        print ">> " . $line;
      }
  }

1;

I cannot find documentation on what -t does. I mean I can find that it tests whether Filehandle is opened to a tty. But I'm not sure what that means. Nor do I understand why I must use STDIN without the angle operator.

In other words,

if ( -t <STDIN> )

either hangs if not on the receiving end of a pipe or I lose the first line of my piped data. (That I lose the first line of my piped data actually makes sense to me.)

I was expecting something different. I was expecting that I should write the code as follows:

#!/usr/bin/perl
use strict;
use warnings;

if ( -t STDIN )
  {
    while ( my $line = <STDIN> )
      {
        print ">> " . $line;
      }
  }
else
  {
    print "NOTHING PIPED IN\n";
  }

1;

Solution

  • <STDIN> is short for readline( STDIN ). You don't want to read a line; you want to check if the file handle is connected to a terminal.

    A tty is a terminal.[1] In unix, ttys are a specific class of character device. (More on this later.) It basically refers to an interactive interface with a user.[2]

    -t, documented as function -X, tests is the provided file handle is connected to a tty. For example, it might end up calling isatty internally.

    Earlier, I said ttys are a specific class of character device. This means that some character devices aren't ttys. But more importantly, it also means that file handles that aren't character devices also can't be ttys. Other types of file handles include "plain file", directory, socket, pipe and block device. -t will return false for a handle connected to any of these.

    $ printf '' >file
    
    $ perl -Mv5.14 -e'say -t STDIN ? 1 : 0'              # Happens to be a tty.
    1
    
    $ perl -Mv5.14 -e'say -t STDIN ? 1 : 0' </dev/tty    # Definitely a tty.
    1
    
    $ perl -Mv5.14 -e'say -t STDIN ? 1 : 0' </dev/null   # Char dev that's not a TTY
    0
    
    $ perl -Mv5.14 -e'say -t STDIN ? 1 : 0' <file        # Plain file.
    0
    
    $ printf '' | perl -Mv5.14 -e'say -t STDIN ? 1 : 0'  # Pipe
    0
    

    1. It's short for "teletype" and "teletypewriter", which harks back to the days where people connected remotely to computer via typewriters-printer combos instead of plugging keyboard and monitors into them.
    2. There also exists a type of tty called pseudo-tty which allows programs to communicate with other programs as if they were users.