There is a script run via cron with the following line:
0 * * * * (/var/script.sh | tee -a /var/script.log)
How do I rewrite the cron entry to capture both normal stdout
and error stderr
output? They are to be placed in different files.
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.
Sign up to join this communitycron
jobs are run by sh
by default, and regardless of whether sh
is a Bourne or POSIX sh
or compatible, the syntax would be:
0 * * * * /var/script.sh 2>&1 | tee -a /var/script.log
Where the |
causes the two commands on either side to be run in parallel connected with a pipe with the stdout (fd 1) of the left one plugged to the writing end of the pipe and the stdin (fd 0) of the right one plugged to the reading end.
By adding 2>&1
to the left one, we have the fd 2 (stderr) point to the same resource as pointed out by fd 1: the writing end of the pipe, so both the normal and error output of script.sh
will go to the pipe to tee
.
tee
will write it both to its stdout (which in the case of a cron job is either a pipe or temp file that will be used to send an email to the user) and to script.log
.
That syntax also works in the fish
shell.
With (t)csh
, zsh
or bash
(4.0 or newer), you can also do:
SHELL=/bin/zsh # or tcsh, bash ...
0 * * * * /var/script.sh |& tee -a /var/script.log
(where SHELL=/bin/zsh
is how you tell cron
to use a different shell to interpret the command lines).
In fish
, you'd use &|
instead of |&
.
With rc
(the once to be successor of sh
) or derivatives, the syntax is:
SHELL=/bin/rc # or es, akanga
0 * * * * /var/script.sh >[2=1] | tee -a /var/script.log
With zsh
, you can also do without tee
thanks to its MULT_IOS
feature:
SHELL=/bin/zsh
0 * * * * /var/script.sh >&1 2>&2 >>& /var/script.log
Which would also have the benefit of preserving script.sh
's exit status, and also keeps script.sh
's stderr going to the original stderr in addition to script.log
(though that doesn't make much of a difference in a cron job where stdout and stderr generally go to the same place anyway).
A few Bourne/POSIX like shells (bash
, ksh93
, zsh
, yash
, mksh
at least) have a pipefail
option which you can use to be able to report errors in any component of a pipeline:
SHELL=/bin/ksh # or zsh, bash...
0 * * * * set -o pipefail && /var/script.sh 2>&1 | tee -a /var/script.log
cron wants to tell you about any cron job that produces output. It wants to email the results to you. If you don't want to receive email from cron, ensure your jobs produce no output: don't use tee
, just redirect the output
0 * * * * /var/script.sh >> /var/script.log 2>&1
Note also I removed the parentheses: there's no point in running the script in a subshell.
stderr
output to the file.