This appears to work:
#!/usr/bin/env zsh
exec >&1 >>log.txt 2>&1
print -u1 stdout stuff
print -u2 stderr info
Testing:
> ./testexec
stdout stuff
stderr info
> cat log.txt
stdout stuff
stderr info
Change >>log.txt
to >log.txt
if you want to overwrite the file each time rather than appending to it. This also worked with >>$log
when the log
variable was set to a filename. The multios
syntax is described in this answer, and there's a bit more info here.
This does require that multios
be enabled - as you noted, that is the default. set -o
(nb: set, not setopt) can be used to display the status of all of the options; setopt
without parameters only lists options that have been changed from the default.
Some notes on the pieces:
exec ...
- theexec
builtin will replace the current shell with a new process. When it's invoked like this without a command argument, the replacement is another instance ofzsh
, with the redirects set to the new values in the command line. The effect is almost identical to invoking a command with those redirects on the command line, e.g.
./testexec{print abc;print -u2 def} >&1 >>log>log.txt 2>&1
.>&1
- redirectstdout
tostdout
. Yes, it's a bit redundant - this is a signal tomultios
that we want to continue to sendstdout
to its current destination (which is probably the terminal) in addition to the following redirects.>>log.txt
- add a redirect ofstdout
to the filelog.txt
in append mode. This is amultios
-specific behavior; now the standard output is being sent to two places, via a mechanism inzsh
that looks a lot liketee
.2>&1
- sendstderr
to the same place asstdout
. Thanks to the previous two redirects,stdout
is going to two places, so this also sendsstderr
output to the file and (probably) the terminal.print -u<n>
- just for testing. The-u
option sends the output to file descriptor<n>
.