8

I have a large bash script and I want to globally redirect all stdout/stderr to a file, except some progress indicators such specific echo messages. For example, consider the following script

#!/bin/bash

# all stdout and stderr go to out.log
exec &> out.log

special_echo "starting"

# many other commands go here
# which output both the stdout and stderr
# also some special_echo commands in here,
# for example
echo "msg1"
special_echo "middle"
echo "msg2" >&2

special_echo "finished"

When it is run the output should be

$ ./script
starting
middle
finished
$

But, out.log should contain

msg1
msg2

How to implement special_echo functionality? I've tried using a file descriptor and echoing to that but can't get it to display on the screen.

Is there a way to achieve this without appending redirection to every line or doing something like in this answer?

Community
  • 1
  • 1
user2660278
  • 348
  • 2
  • 6

3 Answers3

12

Yes, using another file descriptor is the way to go:

#!/bin/bash

exec 3>&1
special_echo () {
    echo "$@" >&3
}

exec &> out.log

special_echo "starting"
echo "msg1"
special_echo "middle"
echo "msg2" >&2
special_echo "finished"
choroba
  • 231,213
  • 25
  • 204
  • 289
1

Redirect to /dev/tty, which is the controlling terminal. Works also for input.

Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
1

At the start of your script save your stdout & stderr as custom descriptors.

# all stdout and stderr go to out.log
exec 3>&1 4>&2 &>out.log

you can use 3>/dev/stdout instead of 3>&1 et cetera, or split it into multiple exec redirect lines, if that's more readable to you.

Then all you need to do is use them

echo "starting" >&3
echo "msg1"
echo "middle"   >&3
echo "msg2" >&2
echo "finished" >&3

you cant use >/dev/stdout because it actually got overridden with exec, custom descriptors are necessary.

The best part about this is that you still have access to the original stderr, which you wont with /dev/tty

echo "special error" >&4

And you can completely undo your redirection for the rest of the script if that's something you need.

#restore original
exec >&3 2>&4

echo "this is special now"

No functions, no variables, just works.

Hashbrown
  • 12,091
  • 8
  • 72
  • 95