Your standard input is a tty in a number of surprising cases
Every once in a while, someone writing a program decides that
checking to see whether standard input is a tty (via isatty()
)
is a great way of determining 'am I being run interactively or
not?'. This certainly sounds like a good way to do this check if
you aren't familiar with Unix and don't actually test any number
of situations, but in fact it is wrong almost all of the time.
For a start, this is wrong if your command is just being run in a shell script. Commands run from a shell script inherit the script's standard input; if you just ran the script itself from a shell command line, well, that's your tty. No Unix shell can behave differently because passing stdin to script commands is what lets shell scripts work in the middle of pipelines. But plain commands are the obvious case, so let's go for an odder one:
var=$(/some/command ....)
You guessed it: /some/command
inherits the shell's standard input
and thus may have its standard input connected to your tty. Its
standard output is not a tty, of course; it's being collected by
the shell instead.
Now let's talk about GNU Make. Plain commands in Makefiles are like
plain commands in shell scripts; make
gets your standard input
and passes it to commands being run. In my opinion this is far less
defensible than with shell scripts, although I'm sure someone has
a setup that uses make
and a Makefile in the middle of a pipeline
and counts on the commands run from the Makefile being able to read
standard input. Still, I suspect a certain number of people would
be surprised by that.
GNU Make has a feature where it can run a shell command as it parses the Makefile in order to do things like set up the value of Makefile variables. This looks like (in the simple version):
AVAR := $(shell /some/command ...)
This too can have isatty(stdin)
be true. Like the shell, GNU Make
passes its standard input down even to things being run via command
substitution.
The short form version of this is almost anything that's run even
indirectly by a user from their shell prompt may have standard input
being a tty. Run from a shell script that's run from three levels
of Makefiles (and make
s) that are started from a shell script
that's spawned from a C program that does a system()
? Unless
there's a pipeline somewhere in there, you probably still have
standard input connected to the user's tty.
It follows that checking isatty(stdin)
is a terrible way of seeing
whether or not your program is being run interactively, unless the
version of 'interactively' you care about is whether you're being
run from something that's totally detached from the user, like a
crontab or a ssh remote command execution (possibly an automated
one). Standard input not being a tty doesn't guarantee this, of
course, but if standard input is a tty you can be pretty sure that
you aren't being run from crontab et al.
(The corollary of this is that if you're writing shell scripts and
so on, you may sometimes want to deliberately disconnect standard
input from what it normally would be. This doesn't totally stop
people from talking to the user (they can always explicitly open
/dev/tty
), but at least it makes it less likely to happen by
more or less accident.)
|
|