Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirected output hangs when using tee

Tags:

linux

bash

unix

I'd like to provide an optional logging parameter in a bash script, and would like to use exec to tee a pipe from the beginning. However, opening the tee process is causing the script to hang, I believe because stdout is not closed:

# Output to a log file, if set
if [[ $OPT_LOG ]]; then
    exec > >(tee -a $OPT_LOG)
fi

I've attempted to close with:

exec >&-

But it still hangs - is there another way to properly close tee so the script will exit properly at the end of execution?

like image 274
Andrew Vaughan Avatar asked Jun 06 '15 20:06

Andrew Vaughan


2 Answers

It seems like, for some reason, using tee stops the prompt ($PS1) from appearing because the shell script has not exited. As a workaround, I generally use a short sleep after calling tee.

#!/bin/bash

exec > >(tee -a mylog)
sleep .1
# my code
like image 123
Eugeniu Rosca Avatar answered Nov 02 '22 04:11

Eugeniu Rosca


Converting comments into an answer, with minor edits.

I noted:

The following code exits OK for me on Mac OS X 10.10.3.

OPT_LOG=file.name
if [[ $OPT_LOG ]]
then exec > >(tee -a $OPT_LOG)
fi
for ((i = 0; i < 10; i++))
do
    echo "Logging message $i at $(date)"
    sleep 1
done

Your problem is probably in the code you've not shown. What commands are you running? What do you get from bash -x yourscript.sh?

And chatraed observed

If you remove the sleep and date calls from your example, the script does not exit properly any more, as told by Andrew:

OPT_LOG=file.name;
if [[ $OPT_LOG ]]; then exec > >(tee -a $OPT_LOG); fi;
for ((i = 0; i < 10; i++)); do echo "Logging message $i"; done

And I responded:

Now that's an interesting observation! I can reproduce your result.

I experimented a bit:

  • I added a pwd before the for loop with 'just the echo', and that didn't affect things (but pwd is probably a Bash built-in).
  • I added bash -c 'exit 0' (which is not a built-in) and the code terminated OK.
  • I tried using > >(tee -a $OPT_LOG &) and the output didn't appear — on screen or in the file. (I find this surprising, but it was an attempted workaround, not a major part of the work.)

My impression is that Andrew has found a bug in Bash that could viably be reported. (See the Bash manual on Reporting Bugs for how to do that.) I don't think that not exiting simply because no external commands have been executed since the I/O redirection is OK. I can confirm that Apple's Bash 3.2.57 has the problem; so does Bash 4.3.27 (which I built for myself so it is patched with the fixes to the ShellShock bug).

It is relatively unusual that a shell script does not invoke any external command, and invoking any external command after the exec redirection seems to suppress the bug.

It also confirms that chatraed's workaround works, albeit more slowly than minimally necessary. For production use, I'd choose sleep 0, which is most of a tenth of a second faster than sleep 0.1. And sleeping for a fraction of a second only works on some systems; classically (and according to POSIX), sleep does not sleep for fractional seconds. OTOH, if you write sleep 0.1, on systems without support for fractional seconds, including the leading 0 probably makes it sleep for zero seconds anyway; writing .1 might or might not have the same effect.

like image 35
Jonathan Leffler Avatar answered Nov 02 '22 06:11

Jonathan Leffler