Home > Software engineering >  Is it possible to redirect stdout to a non-stderr stream inside a shell script?
Is it possible to redirect stdout to a non-stderr stream inside a shell script?

Time:04-14

This is related but orthogonal to my earlier question.

Consider the following shell script, which I'll put in a file called Play.sh.

#!/bin/bash

echo "Send me to standard out"
echo "Send me to standard err" >&2

When I run this, I can redirect stdout and stderr.

$ ./Play.sh  > /dev/null
Send me to standard err
$ ./Play.sh  2> /dev/null
Send me to standard out

All this works as desired.


I would like to open a third stream and force echo to output to the third stream, so I can filter to it. Something like the following:

#!/bin/bash

echo "Send me to standard out"
echo "Send me to standard err" >&2
echo "Send me to stream 3" >&3

Unfortunately, the third stream does not attach to /dev/tty by default.

Observed Output

# This works as desired
./Play.sh  3> /dev/null
Send me to standard out
Send me to standard err

# This does not work as desired.
$ ./Play.sh
Send me to standard out
Send me to standard err
./Play.sh: line 5: 3: Bad file descriptor

Desired Output

# This works as desired
./Play.sh  3> /dev/null
Send me to standard out
Send me to standard err

# I would like stream 3 to go to /dev/tty ONLY when not redirected.
$ ./Play.sh
Send me to standard out
Send me to standard err
Send me to stream 3

Is there an incantation I can use inside the shell script to make the third stream point at /dev/tty if and only if the parent process does not redirect it?

Effectively, I'd like the third stream to behave exactly the same way as stdout and stderr: Default to /dev/tty and allow the parent to redirect.

CodePudding user response:

The file descriptor 3 inside Play.sh and the 3 in ./Play.sh 3> /dev/null are not related because they belong to different processes.

What you can do is to pass the destination into Play.sh:

#!/bin/bash

third_stream="$1"
exec 3>$third_stream

echo "Send me to standard out"
echo "Send me to standard err" >&2
echo "Send me to stream 3" >&3

then call it with :

./Play.sh /dev/null

CodePudding user response:

For completeness, based on tripleee's comment, what I actually wanted was the following:

#!/bin/bash

echo "Send me to standard out"
echo "Send me to standard err" >&2


if ! (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is not open
  # Option 1: Redirect fd3 to stdout.
  exec 3>&1
  # Option 2: Redirect fd3 to /dev/null.
  # exec 3>/dev/null
fi

# No error here.
echo "Send me to stream 3" >&3

With this modification, a parent process can redirect 3 when it desires to do so, and otherwise have it as part of stdout.

$ ./Play.sh
Send me to standard out
Send me to standard err
Send me to stream 3

$ ./Play.sh 3> /dev/null
Send me to standard out
Send me to standard err

  • Related