Ideally, this would be scriptable in shell, but Perl or Python would be fine.
C code could be helpful, but probably fails cost/benefit.
I recognize that redirection to a FIFO (named pipe) may be indistinguishable from a real pipe,
and that is enough of an edge case that I don't really care.
Strict POSIX solutions are best, UNIX/Linux variant-independent are next best, but at least something that works on Darwin (MacOS X) is what I need right now.
Before you write your answer - I already know about test -t
- that will tell me whether stdout is a terminal (in which case it is definitely not a pipe - but it will not tell me whether stdout has been redirected to a file, non-terminal character device, or UNIX-domain socket rather than a pipe.
Intended use case: I have a command that should be run within backquotes in the shell,
so that it can output commands that set environment variables.
I would like the command to abort with an error if stdout is not redirected to a pipe,
as in that case it definitely wasn't invoked by eval `mycommand`;
.
If there's some special environment variable that a shell will set when running a command within backquotes that would be helpful, but as it is likely to be specific to bash or zsh or something, pipe-detection is more important.
(my comment turned to answer per Alex Dupuy's suggestion)
Since you said you can use perl, I guess you can use its -p file test operator (perldoc.perl.org/functions/-X.html); something like perl -e 'exit(-p STDOUT ? 0 : 1);'
will tell you if stdout if a pipe or a fifo (without distinguishing between them).
Fwiw, calling that from the shell is exactly the reason why I used perl -e
:)
You could do an fstat
to the file descriptor and check returning structure, for example, st_mode
= 0x1000 (S_IFIFO) indicates a Named Pipe.
Example with Python:
from __future__ import print_function
import sys
import os
print(os.fstat(sys.stdout.fileno()), file=sys.stderr)
Output on windows:
C:> python test_fd.py | more
nt.stat_result(st_mode=4096, st_ino=0L, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=0L, st_atime=0L, st_mtime=0L, st_ctime=0L)
C:> python test_fd.py > test_fd.txt
nt.stat_result(st_mode=33206, st_ino=16888498602769633L, st_dev=0, st_nlink=1, st_uid=0, st_gid=0, st_size=0L, st_atime= 1401119520L, st_mtime=1401119520L, st_ctime=1401119349L)
The stat
command works on MacOS X and (with some finessing of variant implementations) other Linux/Unix variants as well, so it is a solution for me:
FORMOPT=
TYPEFORM=
stat() {
if [ -z "$FORMOPT" ]; then
if /usr/bin/stat --version >/dev/null 2>&1; then
FORMOPT=--format
TYPEFORM=%F
else
FORMOPT=-f
TYPEFORM=%HT
fi
fi
case $1 in
type) FORMARG="$FORMOPT $TYPEFORM" ; shift ;;
esac
/usr/bin/stat -L $FORMARG "$@"
}
exec 9>&1
case `stat type /dev/fd/9` in
[Ff]ifo*) echo stdout is a pipe ;;
*) echo stdout is not a pipe ;;
esac
In Python:
import sys
print sys.stdout.isatty()
Examples:
$ ./a
True
$ ./a | less
False
There is a Linux-specific solution that does what I want:
exec 9>&1
case `readlink /dev/fd/9` in
pipe:\[*\]) echo stdout is a pipe ;;
*) echo stdout is not a pipe ;;
esac
but that isn't the answer I am looking for as I want this to run on xBSD as well.
I've made this answer community wiki, so feel free to improve it,
but if you have a non-Linux solution (or a Linux-specific solution using another technique),
please create a new answer.