Why does `exec 2>&1` fail in this bourne shell script?
Solution 1
Your script is not failing - it's working just fine. Your understanding is correct in that exec >logfile; exec 2>&1
redirects both standard output and standard error to logfile
. So, you should be looking in the log file and not your terminal for output and error. If you perform those redirections directly in your current shell, it looks like your shell has frozen because you sent all the output away from your terminal.
Note that the output from the xtrace
(set -x
) option also goes to standard error, which is always file descriptor 2 ... which you sent away to the log file. You should find the rest of it after the exec 2>&1
in there.
Solution 2
This form of exec (i.e. without a command) is used to redirect all subsequent output from the current shell interpreter.
from bash's built-in help:
$ help exec exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...] Replace the shell with the given command. Execute COMMAND, replacing this shell with the specified program. ARGUMENTS become the arguments to COMMAND. If COMMAND is not specified, any redirections take effect in the current shell. [...]
I use exec &> logfile
to redirect stdout and stderr at the same time. e.g. most of my backup and rsync wrapper scripts (or any script that produces lots of output that I might want to examine in detail later) start with something like this:
BNAME=$(basename "$0" .sh)
LOGFILE="/tmp/$BNAME.log"
savelog "$LOGFILE"
exec &> "$LOGFILE"
I then run the script from cron, or in the background and use tail -F
to watch the logfile as the script is running. The savelog lets me keep the output from the last 7 runs (7 by default, savelog -c
can be used to change that).
Related videos on Youtube
Mark Norgren
Updated on September 18, 2022Comments
-
Mark Norgren over 1 year
I am porting an old ksh script to Bourne shell. The old ksh script contains the following code:
#!/bin/sh tmpLog=/var/tmp/logfile.$$ exec 1> $tmpLog exec 2>&1 eval $* another_command_1 another_command_2
From what I read, it seems that these two exec statements are intended to execute $*, another_command_1, another_command_2 and all following commands; and then redirect all STDERR and STDOUT from those commands into
/var/tmp/logfile.$$
. However, when I run this in a script the script fails afterexec 2>&1
.stefanl@host:~ $ sh -xv ./output.sh echo "Hello" #!/bin/sh tmpLog=/var/tmp/logfile.$$ + tmpLog=/var/tmp/logfile.39918 exec 1> $tmpLog + exec exec 2>&1 + exec stefanl@host:~ $
And when I run this on the commandline, my shell freezes after I execute
exec 2>&1
:stefanl@host:~ $ tmpLog=/var/tmp/logfile.$$ stefanl@host:~ $ exec 1> $tmpLog stefanl@host:~ $ exec 2>&1 ### FREEZE ###
My questions:
- What is
exec 2>&1
supposed to do? - Why is it failing for me?
- What is
-
Mark Norgren almost 12 yearsAh, thanks! And if I open a new window and do a
tail -f /var/tmp/logfile.39918
I see everything that I type in the old window. Clever trick! -
jw013 almost 12 yearsThe question specifically targets the Bourne shell. Bourne doesn't have the
&>
operator, nor is quoting thebash
man page terribly helpful. The part about cron is irrelevant. -
Alessio almost 12 yearsIt still clearly answered his first question 'What is exec 2>&1 supposed to do?` which hadn't actually been answered yet. Assuming a reasonable ability to understand, generalise and extrapolate doesn't deserve a -1. I tend to prefer providing a general explanation instead of just cargo-cult magic to a specific question. BTW, it was the bash help, not the bash man page...it just happened to be the most convenient and succinct documentation to hand, and the relevant point about taking effect in the current shell applies to all versions of sh.
-
jw013 almost 12 yearsI don't see any part in your answer that specifically explains what
exec 2>&1
does? I generally downvote answers that encourage misconceptions, whether explicitly or implicitly (in your case, implyingsh
=bash
), because not every person who reads your answer in the future will know the difference. bash is based on sh but in practice there are many differences (like the&>
: putting that in a Bourne shell script will probably break it). Also, none of those options thatbash
exec
accepts are likely to be valid. -
jw013 almost 12 yearsThat sentence is no better than "
exec >logfile; exec 2>&1
redirects both standard output and standard input to logfile". You're still confusingbash
withsh
. -
Alessio almost 12 yearsQ. "What is exec 2>&1 supposed to do?". A. "This form of exec (i.e. without a command) is used to [...]". There is no confusion between bash and sh in that sentence. It is true for all variants of sh. You ignored his first question and answered his second, I ignored his second and answered his first. You're judging my answer by the question you answered, not by the one I answered.
-
Kevin Fegan over 10 years"... sends both standard output and standard input to
logfile
, ..." should be "standard output and standard error". I'll edit to make the correction. -
Yu Shen about 7 yearsI appreciate your help! 'exec &> logfile' is the most useful information. The idea of extracting script basename 'BNAME=$(basename "$0" .sh)` is also good, but it's not relevant to the topic. It might be better to separate the additional help as comments.