Reputation: 373
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.
Upvotes: 0
Views: 51
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