PilotOfSparrow PilotOfSparrow - 2 months ago 8
Linux Question

Can't redirect interactive shell's output to file with a script

I trying to write simple output logger. And it's just refuse to work. I can swear, it worked once and it was beautiful.

It's practice, so I don't want to use pre-build bash tools. (like

script
)

Code:

#!/bin/bash
# create_log.sh

exec 6>&1

exec &> log

s

a=0

while true
do
sleep 1

echo love

((a++))
if [ "$a" -eq 1000 ]
then
break
fi

done

exec 1>&6 6>&-

echo "Stopped doing love"


I run this script in console
. /create_log.sh &


And as long as the cycle turns,
stdout
and
stderr
should be redirected to log file. But they simply doesn't.

Log file full of love, but I simply can not get date. (or any other output from console)

P.S. If I just type
exec > log
in console it's work perfectly.

Answer

An approach needs to be run natively in the shell for which you intend to redirect output, not in any subprocess of that shell. Running anything with a & as the command separating it from the next command puts it in a subprocess, rather than running in the shell itself.

Consider this pair of functions (for bash 4.1 or newer):

# for this example, consider this content to belong to file-with-functions.bash

start_redir() {
    exec {orig_stdout}>&1
    exec > >(tee log >&$orig_stdout)
}

end_redir() {
    [[ $orig_stdout ]] || {
      echo "Not redirected with start_redir previously" >&2
      return 1
    }
    exec 1>&$orig_stdout
    exec {orig_stdout}>&-
}

...this can be used as follows:

. ./file-with-functions.bash # source these functions into the current shell; no &

start_redir
ls
end_redir

You can put these functions in a file that you source, but that sourcing needs to be done in the foreground, as putting anything in the background makes it happen in a subprocess, not the shell you're using itself.

Comments