Is there a way to find out the name of the file stdout is redirected to in Perl?

Viewed 171

Same question as this one written for Python, but asking again for Perl since - well - they are different scripting languages and perhaps different feature sets.

In general, I want to detect if my Perl script's STDIN our STDOUT is being redirected (preferably the former). Instead of knowing the filename, I'd rather want to know if the caller is using redirection so that I can present them with a 'help text' if they're not.

Preferably platform independent, but I only foresee this script being run on macOS or Linux.

2 Answers

You can check if a handle is a terminal or not using -t.

if (-t STDOUT) {
   say "Terminal";
} else {
   say "Not a terminal or an error occured"; 
}

or

defined( my $rv = -t STDOUT )
   or die("Can't check if STDOUT is a terminal: $!\n");

if ($rv) {
   say "Terminal";
} else {
   say "Not a terminal"; 
}

Terminal or not, you can find out the kind of file it is.

stat(STDOUT)
   or die("Can't stat STDOUT: $!\n");

if    (-f _) { say "Plain file";       }
elsif (-p _) { say "Pipe";             }
elsif (-S _) { say "Socket";           }
elsif (-c _) { say "Character device"; }
elsif (-b _) { say "Block device";     }
else         { say "Something exotic"; }  # Impossible?

A terminal will be a character device, but not all character devices are terminals.


It isn't possible to determine to which file associated (if it even has a name) in a system-independent fashion (if at all), modules aside.

On Linux, you can use /proc

$ perl -e'warn readlink("/proc/$$/fd/".fileno(STDOUT))."\n"'
/dev/pts/0

$ perl -e'warn readlink("/proc/$$/fd/".fileno(STDOUT))."\n"' >a
/tmp/ikegami/a

$ perl -e'unlink "a";
          warn readlink("/proc/$$/fd/".fileno(STDOUT))."\n"' >a
/tmp/ikegami/a (deleted)

$ perl -e'warn readlink("/proc/$$/fd/".fileno(STDOUT))."\n"' |cat
pipe:[27807]
$ perl -le'print(-t STDIN ? "terminal" : "file")'
terminal
$ perl -le'print(-t STDIN ? "terminal" : "file")' < /dev/null
file
$
Related