Skip to content
shellmap

crontabSchedule recurring commands on Unix via per-user crontab files across all 5 shells

Equivalents in every shell

Bashunix
crontab -e

Edits the current user's crontab in `$EDITOR`. Format per line: `m h dom mon dow command`. Examples: `*/5 * * * * /path/script.sh` (every 5 min); `0 3 * * * backup.sh` (3 AM daily); `0 0 * * 0 weekly.sh` (Sunday midnight). `crontab -l` lists, `crontab -r` removes (no confirmation — dangerous). The PATH inside cron jobs is MINIMAL — typically `/usr/bin:/bin`. Set `PATH=...` as a line in the crontab itself or use absolute paths.

Zshunix
crontab -e

Same Unix binary. macOS gotcha: macOS 10.15+ runs many `cron` jobs under sandboxed restrictions — your job may need Full Disk Access granted to `cron` in System Settings → Privacy & Security to touch user files. The Apple-blessed alternative is `launchd` plists in `~/Library/LaunchAgents/` (richer triggers, including "run on file change", "run at login", "run after sleep") — `crontab` still works but is on the deprecated path.

Fishunix
crontab -e

Same external. Fish-specific: the COMMAND in crontab is run via `/bin/sh` (POSIX), NOT fish. So a crontab line like `0 * * * * source ~/.config/fish/config.fish; my-fish-function` does NOT work — sh doesn't understand fish. Wrap fish scripts: `0 * * * * /usr/bin/fish -c "my-fish-function"`. Or, put the actual logic in a portable shell script and invoke that.

PowerShellwindows
Register-ScheduledTask -TaskName Backup -Trigger (New-ScheduledTaskTrigger -Daily -At 3am) -Action (New-ScheduledTaskAction -Execute pwsh -Argument "-File C:\backup.ps1")

Windows scheduled tasks via the `ScheduledTasks` module. Richer trigger surface than cron (logon, system idle, on-event-log-event, at-startup, etc). `Get-ScheduledTask` lists, `Get-ScheduledTaskInfo -TaskName x` shows last/next run times. Multi-step task setup: `New-ScheduledTaskTrigger` + `New-ScheduledTaskAction` + `New-ScheduledTaskPrincipal` (run-as identity) + `New-ScheduledTaskSettingsSet` (battery / network conditions) + `Register-ScheduledTask` to install. Programmatic; the GUI is `taskschd.msc`.

cmd.exewindows
schtasks /create /sc DAILY /st 03:00 /tn Backup /tr "C:\backup.bat"

`schtasks` is the Windows builtin equivalent of `crontab`. Flags: `/sc <schedule>` (MINUTE / HOURLY / DAILY / WEEKLY / MONTHLY / ONCE / ONSTART / ONLOGON / ONIDLE), `/st HH:MM` start time, `/tn` task name, `/tr` command to run. `schtasks /query` lists, `schtasks /delete /tn Backup /f` removes. The older `at` command still works for simple "run once at this time" but is deprecated since Win8.

Worked examples

List current scheduled tasks

Bash
crontab -l
Zsh
crontab -l
Fish
crontab -l
PowerShell
Get-ScheduledTask
cmd.exe
schtasks /query

Schedule a daily 3 AM backup

Bash
(crontab -l 2>/dev/null; echo "0 3 * * * /path/backup.sh") | crontab -
Fish
crontab -e   # then add: 0 3 * * * /path/backup.sh
PowerShell
Register-ScheduledTask -TaskName Backup -Trigger (New-ScheduledTaskTrigger -Daily -At 3am) -Action (New-ScheduledTaskAction -Execute pwsh -Argument "-File C:\backup.ps1")
cmd.exe
schtasks /create /sc DAILY /st 03:00 /tn Backup /tr "C:\backup.bat"

Remove a scheduled task

Bash
crontab -l | grep -v "/path/backup.sh" | crontab -
PowerShell
Unregister-ScheduledTask -TaskName Backup -Confirm:$false
cmd.exe
schtasks /delete /tn Backup /f

Gotchas

  • `crontab -r` removes the ENTIRE crontab without confirmation. Many Unix admins have lost weeks of automation by typing `crontab -r` when they meant `crontab -e`. Defense: always `crontab -l > ~/cron.backup` before edits, and consider aliasing `crontab` to a wrapper that takes a snapshot first. Some Linux distros patch in a `-i` (interactive confirm) flag — check `crontab --help`.
  • Cron's environment is NEARLY EMPTY — no `PATH`, no `HOME`, no DISPLAY, none of the variables your interactive shell has. A script that runs fine when you `bash script.sh` from the terminal will SILENTLY FAIL from cron with `command not found`. Fix: set absolute paths (`/usr/bin/node` not `node`) or put `PATH=/usr/local/bin:/usr/bin:/bin` as the first non-comment line of the crontab.
  • Cron output goes to MAIL by default — if `mail` is configured, you get an email per job. If not, the output is silently dropped. Most servers in 2026 do not have local mail configured, so cron output vanishes — your script reports "it worked!" to /dev/null. ALWAYS redirect explicitly in the crontab line: `0 3 * * * backup.sh >> /var/log/backup.log 2>&1`.
  • macOS sandbox restrictions: in 10.15+, `cron` jobs that touch `~/Documents`, `~/Desktop`, etc fail unless `cron` itself has Full Disk Access. Grant it via System Settings → Privacy & Security → Full Disk Access → `+` → `/usr/sbin/cron`. This is the most common "my crontab worked on Linux but does nothing on macOS" cause. Apple's preferred path is `launchd` (`launchctl load ~/Library/LaunchAgents/foo.plist`), which has its own permission model.
  • Windows `schtasks /create` defaults to running as the CURRENT USER, but the task only runs when that user is logged on. To run "at 3am every night even when logged off", add `/ru SYSTEM` (run as SYSTEM, no logon required) or `/ru <user> /rp <password>` (cached credentials — credential dialog on machines with Credential Guard). The pwsh equivalent is `Register-ScheduledTask -Principal (New-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount)`.

WSL & PowerShell Core notes

pwsh`Register-ScheduledTask` and family live in the `ScheduledTasks` module which is Windows-only. On Linux / macOS pwsh, shell out to `crontab`. A portable pwsh wrapper: `if ($IsWindows) { Register-ScheduledTask ... } else { & crontab -l | ForEach-Object { ... } }` — but the underlying scheduling models are different enough that a true cross-platform abstraction is rare. For container-based scheduling, both worlds typically converge on a third option (Kubernetes CronJob, AWS EventBridge, etc).
WSLWSL2 with `systemd` opt-in (post-2022 distros, `[boot] systemd=true` in `/etc/wsl.conf`) supports cron normally — `crontab -e` works and jobs run on schedule WHILE WSL is running. The big catch: if NO WSL window is open, the WSL distro shuts down after ~8 sec of idle, killing cron. Workarounds: keep a WSL terminal open, run `wsl --shutdown` then `wsl` after each Windows reboot to keep the distro alive, or use Windows `schtasks` to schedule the work and have it `wsl <command>` into your distro.

Common tasks using crontab

Related commands