2

Let us say I redirect the STDOUT, STDERR of a program to files.

./script.sh 1> output.log 2> error.log

Can the running program find this out i.e. know the paths to these files?

4 Answers4

5
{ readlink /dev/fd/[1,2] ; echo "out" ; } >./file 2>./error    
{ readlink /dev/fd/0 ; cat ; } <./file

OUTPUT:

/home/mikeserv/file
/home/mikeserv/file
/home/mikeserv/error
out

{ readlink /proc/$$/fd/[1,2] ; echo out ; } >./file 2>./error
{ readlink /proc/$$/fd/0 ; cat ; } <./file

OUTPUT:

/home/mikeserv/file
/home/mikeserv/file
/home/mikeserv/error    
out
mikeserv
  • 58,310
2

You can call lsof to list the open files of the shell process. Use -a -p $$ to limit the output to the shell process ($$), -d 1 to limit the output to file descriptor 1 (for instance), and -F n to print the output in parseable form. Here's a shell snippet that copes with arbitrary characters in file names:

output_file=$(lsof -a -p $$ -d 1 -F pn; echo .)
output_file=${output_file%.}
output_file=${output_file#n}

If the file name doesn't contain a newline, you'll be able to get away with output_file=$(lsof -a -p $$ -d 1 -F pn | sed -n '2s/.//p').

Note that the file name may not always exist, in particular if the file has been deleted.

Under Linux, another way to access the file names is through /proc/$$/fd: /proc/$$/fd/1 is a slightly magic symbolic link to the file opened by the shell on file descriptor 1 (the link works even if the file name returned by readlink doesn't exist, for example in the case of a deleted file).

It is usually a very bad idea to make any use of the information obtained this way. If someone calls your script with the output redirected to a file, they won't like it if you behave differently because of the location of the file, or affect the file in ways other than appending to it. There is one exception: you may want to react differently depending on whether you're writing to a terminal or to something else (pipe, socket, file), for example to display colors or progress indicators on a terminal. There is a specific test to determine whether a file descriptor is connected to a terminal:

if [ -t 2 ]; then
  # stderr is a terminal
  printf 1>&2 '\e[31mError: widget not found\e[0m'
else
  # stderr is not a terminal
  echo 1>&2 'Error: widget not found'
fi
  • This is good - but it depends on lsof, of course. Though not the last bit, obviously. – mikeserv Apr 07 '14 at 21:10
  • @mikeserv That's the portable way to do it. I do also mention the Linux way. – Gilles 'SO- stop being evil' Apr 07 '14 at 21:14
  • I should clarify - lsof is awesome - its just not always there. Neither is readlink for that matter - though I think the chances are a little better. - I agree. I was writing that addendum before even I knew you had replied. Speaking of readlink - my preference is for realpath, actually. – mikeserv Apr 07 '14 at 21:15
  • In any case, it would be hard to argue you did a worse job of explaining it than anyone else did here - especially me. You've already got my vote. – mikeserv Apr 07 '14 at 21:22
  • @mikeserv If /proc/<pid>/fd is there, then there's a very good chance that you're running Linux (but it could also be Solaris or some other SysV unices) and therefore readlink is available (it's in GNU coreutils and BusyBox). realpath is less common: under Linux, it's a deprecated program from the time before readlink, which may still be installed if some legacy application depends on it. – Gilles 'SO- stop being evil' Apr 07 '14 at 21:23
  • Really? realpath is deprecated? I thought i remembered reading it it was just added to GNU in 2012. Maybe I've got it backwards. – mikeserv Apr 07 '14 at 21:25
  • Looks like we were both right. New tool, same old name. Though i guess just as GNU brings it in, the BSDs let it go... http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=77ea441f79aa115f79b47d9c1fc9c0004c5c7111 – mikeserv Apr 07 '14 at 21:32
0

In your case, STDOUT will be saved to file output.log and STDERR will be saved to file error.log. Both files are saved in the same directory with script.sh.

If you want your program "know the path to these files", you must use absolute path:

./script.sh > /path/to/output.log 2> /path/to/error.log
cuonglm
  • 153,898
0

The output.log and error.log files are created in the current directory i.e. the $PWD variable value. If you want your program to use these files later, just save their directory in a variable before running your script. Here is an example :

OUTDIR=$PWD
./script.sh 1> output.log 2> error.log
# Whatever you want to do else ...
echo The output file : =======
cat $OUTDIR/output.log
echo =========================
echo 
echo The errors file : =======
cat $OUTDIR/error.log
echo =========================
Slyx
  • 3,885