When working with cron jobs, many developers don't realize that cron doesn't automatically use their preferred interactive shell (like bash or zsh). The system uses a minimal shell environment by default, which can lead to unexpected behavior when your crontab contains complex commands or shell-specific syntax.
Most Unix-like systems configure cron to use /bin/sh
as the default shell. This is typically a minimal POSIX-compliant shell, often linked to dash on modern Linux systems:
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Apr 5 2022 /bin/sh -> dash
Consider these common problems that arise:
# In crontab - this will fail if using default /bin/sh
*/5 * * * * echo "Current path: $PATH" > /tmp/cron_test.log
# Array syntax that works in bash/zsh but not in sh
0 * * * * declare -a arr=("a" "b"); for i in "${arr[@]}"; do echo $i; done
You have several approaches to control the shell environment:
1. SHELL Variable in Crontab
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
* * * * * /path/to/your/command
2. Wrapping Commands in Your Preferred Shell
0 3 * * * /bin/bash -c 'source ~/.bashrc; your_command'
3. System-wide Configuration (Advanced)
On some systems, you can modify /etc/default/cron
or equivalent:
# For Debian/Ubuntu systems
CRON_USE_UTC=no
CRON_SHELL=/bin/bash
- Always use absolute paths to binaries
- Set critical environment variables explicitly
- Consider putting complex logic in separate scripts
- Test commands directly with
/bin/sh -c "your command"
Create a test cron job to capture the environment:
* * * * * /bin/bash -c 'env > /tmp/cron_env.log; type bash > /tmp/cron_type.log'
This will help you understand exactly what environment your cron jobs execute in.
Cron executes commands using a minimal environment that typically defaults to /bin/sh
, which on most modern Linux systems is a symlink to either bash or dash. The exact shell used depends on your system configuration.
You can verify which shell your cron uses with:
ls -l /bin/sh
# Common output:
# lrwxrwxrwx 1 root root 4 Apr 5 2022 /bin/sh -> bash
The shell used by cron is typically defined in the crond configuration. For systemd-based systems:
cat /etc/default/cron
# Look for SHELL= parameter
Consider these examples of shell differences:
# bash/zsh array syntax
arr=(1 2 3)
echo ${arr[1]} # bash: 2, zsh: 1 (different index base)
# Process substitution
diff <(echo foo) <(echo bar) # Works in bash, may fail in /bin/sh
1. Always specify full paths to binaries
2. For complex commands, use a script with proper shebang
3. Set environment variables explicitly in crontab
# Example crontab with explicit settings
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
* * * * * /path/to/script.sh
To debug cron jobs:
# Redirect output to examine errors
* * * * * /path/to/command > /tmp/cron.log 2>&1
# Test command with minimal environment
env -i /bin/sh -c "your_command"
Some cron implementations (like Vixie cron) allow per-user shells:
# In /etc/crontab or user crontab
SHELL=/bin/zsh
* * * * * user command
- Debian/Ubuntu: Defaults to dash (/bin/sh)
- RHEL/CentOS: Defaults to bash (/bin/sh)
- FreeBSD: Uses /bin/sh (ash-based)
- MacOS: Uses /bin/sh (bash v3 for compatibility)