Skip to main content

Redirecting Output and Logs

Automation without logs is guessing. Cron jobs often fail silently unless you redirect output. This page shows the essential operators and safe logging patterns for production scripts.

Quick Summary
  • > overwrites; >> appends.
  • 2> captures stderr; 2>&1 merges stderr into stdout.
  • Always log to a directory with correct permissions.
  • Avoid logging secrets.

stdout vs stderr

  • stdout: normal output
  • stderr: errors and diagnostics

Cron only emails output if a mail system is configured. If you want reliable logs, redirect to files.

Core operators

OperatorMeaning
>write stdout, overwrite file
>>write stdout, append to file
2>write stderr, overwrite file
2>>write stderr, append to file
2>&1merge stderr into stdout

Examples

Redirect stdout (overwrite)

redirect-stdout-overwrite.txt
* * * * * /path/script.sh > /var/log/script.log

Redirect stdout (append)

redirect-stdout-append.txt
* * * * * /path/script.sh >> /var/log/script.log

Redirect stderr only

redirect-stderr-only.txt
* * * * * /path/script.sh 2>> /var/log/script.err

Redirect both stdout and stderr to one file

redirect-both-to-one-file.txt
* * * * * /path/script.sh >> /var/log/script.log 2>&1

Separate stdout and stderr

separate-stdout-and-stderr.txt
* * * * * /path/script.sh >>/var/log/script.out 2>>/var/log/script.err

Create a log directory safely

create-log-directory.sh
sudo mkdir -p /var/log/automation
sudo chown root:root /var/log/automation
sudo chmod 750 /var/log/automation

Logging from scripts

If you control the script, you can redirect inside the script.

script-level-logging.sh
#!/usr/bin/env bash
set -euo pipefail

LOG="/var/log/automation/job.log"
exec >>"$LOG" 2>&1

echo "[$(date -Is)] start"
echo "doing work"
echo "[$(date -Is)] done"
warning

Do not write secrets to logs. If your script prints environment variables or command lines containing passwords, fix the script before enabling logging.

systemd timers and journald

systemd services capture stdout/stderr into journald by default. This is one reason timers are easier to debug.

view-systemd-service-logs.sh
journalctl -u wp-backup.service --since '24 hours ago' --no-pager

Send output to syslog/journald with logger

If you prefer central logs, you can send messages via logger.

pipe-output-to-logger.sh
/usr/local/bin/job 2>&1 | logger -t myjob

Cron entry example:

cron-pipe-to-logger.txt
*/10 * * * * /usr/local/bin/job 2>&1 | /usr/bin/logger -t myjob

This avoids log files growing unbounded on disk (but you still need journald retention configured).

Use tee when you want both console and file

tee writes to a file and stdout. It is useful when you run a command interactively and want to capture output.

use-tee-to-capture-output.sh
/usr/local/bin/job 2>&1 | tee -a /var/log/automation/job.log

Debug mode (temporarily)

If you need to see each command as it executes:

bash-debug-mode-example.sh
set -x
command_one
command_two
set +x
warning

Do not leave set -x enabled in scripts that handle secrets. It can print sensitive values into logs.

Log rotation

If you log to files under /var/log, use logrotate.

logrotate-example.txt
/var/log/automation/*.log {
daily
rotate 14
compress
missingok
notifempty
create 0640 root adm
}

Permissions for log files

Logs should not be world-readable if they may contain sensitive paths or identifiers.

fix-log-permissions.sh
sudo chown root:adm /var/log/automation/job.log || true
sudo chmod 640 /var/log/automation/job.log || true

Next steps

  • Cron basics: opt/docker-data/apps/docusaurus/site/docs/server/linux-server/11-automation-task-execution/cron.mdx
  • Cron vs systemd: opt/docker-data/apps/docusaurus/site/docs/server/linux-server/11-automation-task-execution/cron-vs-systemd.mdx