Home > OS >  How to redirect output of an entire ZSH shell script from within script itself?
How to redirect output of an entire ZSH shell script from within script itself?

Time:03-02

So I want to log the output (stdout & stderr) of my non-interactive zsh shell scripts to both stdout and a log file, and to configure that capability from within the script itself.

With bash scripts, I've used the following and it works perfectly:

#!/bin/bash
exec &> >(tee -a /path/to/logfile)

Everything that follows prints both to stdout and to a log file.

But when I change the script to run with #!/bin/zsh, the script will hang when it gets to the exec line.

For example, if the script were this:

#!/bin/zsh
echo 'test'
exec &> >(tee -a /path/to/logfile)

Then when running it, stdout will hang showing this:

 test_script.sh:2> echo test

Annoyingly, in the hanged state, Ctrl-C won't kill the process. Only way I know to get control of my terminal back is to background the process with Ctrl-Z and then kill the pid. (Took me a while to figure that out.)

Anyway, I'd like to know how I can achieve the same result in zsh. Ideally I'd also really like to understand why zsh is behaving differently from bash.

Thank you!

CodePudding user response:

You can't do it that way in zsh, as after exec >(cmd), zsh will be wanting to wait for the process substitution to finish before carrying on.

You could however do:

#! /bin/zsh -
{
  # the whole script goes here
} >&1 >> file.log 2>&1

(no need for tee, as zsh can redirect to multiple files using its all internal teeing when you redirect some fd several times).

CodePudding user response:

I (finally) discovered a way to do this in a way compatible with both bash and zsh:

#!/bin/bash or #!/bin/zsh

exec > >(tee -a path/to/logfile) 2>&1

As a bonus, I also discovered how to change redirection of logging to different files within the same script.

#!/bin/bash or #!/bin/zsh

exec 3>&1
exec > >(tee -a path/to/logfile-1) 2>&1

echo "Copies stdout and stderr to first logfile"

exec >&3
exec > >(tee -a path/to/logfile-2) 2>&1

echo "Copies stdout and stderr to second logfile (and not the first)"

I admit I don't fully understand why this works, and why exec behaves differently with what I tried before, but I'm using this method now with no issues.

Thank you to all who've responded!

  • Related