The one command that sets your Mac's desktop wallpaper from the terminal, across every monitor at once, is this:
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/you/Pictures/bg.jpg"'That is the whole job for most people. It runs as your own user, takes effect immediately, and does not need sudo (the wallpaper is a per-user preference, not a system one). The rest of this page is the detail behind it: the single-display form, the path rules, the caching bug that bites on modern macOS, the Automation permission prompt, and a script that rotates a random wallpaper from a folder.
Set the wallpaper on every display
System Events is the scriptable app that owns the desktops, and every desktop is the part that matters. In the AppleScript object model there is one desktop per attached display, so every desktop hits all of them in one shot:
# Every attached display:
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/you/Pictures/bg.jpg"'A note on Spaces: AppleScript does not expose a separate desktop object per Space, so every desktop enumerates displays, not Spaces. In practice the new wallpaper shows up on every Space too, because macOS keeps the same picture across a display's Spaces by default. If you have deliberately set a different wallpaper per Space, switch to the Space you want before running the command, since the change lands on the active Space's stored picture for that display.
Use an absolute path. AppleScript does not expand the shell ~, so ~/Pictures/bg.jpg passed as a literal string fails silently. If you want to use ~, let the shell expand it before AppleScript ever sees it:
# Let the shell expand the home path, then hand the result to osascript:
osascript -e "tell application \"System Events\" to set picture of every desktop to \"$HOME/Pictures/bg.jpg\""JPEG, PNG, and HEIC all work. The file has to exist and be readable, or you get an error (or, on recent macOS, nothing at all, which is the trap covered below).
The single-display Finder form
The older form many guides still show talks to Finder instead of System Events:
# Primary display only:
osascript -e 'tell application "Finder" to set desktop picture to POSIX file "/Users/you/Pictures/bg.jpg"'This works, but it only sets the main display. On a multi-monitor setup the second screen keeps its old wallpaper. That is the entire reason to prefer the System Events / every desktop form: one command, every screen. Reach for the Finder form only when you specifically want the primary display left to itself, which is rare.
The caching gotcha on modern macOS
Here is the one that wastes an afternoon. On Sonoma (14) and later, macOS reworked how it stores and caches the desktop picture: the state now lives in a per-user store at ~/Library/Application Support/com.apple.wallpaper/Store/Index.plist (a base64-encoded binary plist), which is why older scripts that poked the legacy desktoppicture.db SQLite file no longer have any effect. The practical symptom: if you set the wallpaper to a path, then overwrite the file at that same path with a new image and run the command again, the screen does not change. macOS sees the same path it already cached and skips the redraw.
Two reliable workarounds:
# Option 1: give the new image a new path (a different filename defeats the cache):
cp new-art.jpg "/Users/you/Pictures/bg-$(date +%s).jpg"
osascript -e "tell application \"System Events\" to set picture of every desktop to \"/Users/you/Pictures/bg-$(date +%s).jpg\""
# Option 2: force a refresh after writing to the same path:
killall Dock
# On Sonoma and later, the wallpaper process is its own daemon:
killall WallpaperAgent 2>/dev/nullkillall Dock is the long-standing nudge and is harmless: the Dock relaunches itself in a second. WallpaperAgent is the newer daemon that owns wallpaper rendering on Sonoma+; killing it forces a re-read, and it also relaunches on its own. The 2>/dev/null keeps it quiet on older macOS where that process does not exist. For automation, the cleanest answer is Option 1: write each new wallpaper to a unique filename and the cache problem never arises.
The Automation permission prompt
The first time a given terminal app drives System Events, macOS shows an Automation consent dialog: "Terminal wants to control System Events." This is the TCC (Transparency, Consent, and Control) gate that has applied to Apple Events since Mojave (10.14). Click OK and the grant is remembered per app under System Settings → Privacy & Security → Automation.
Two things to know:
- A non-interactive script (a launchd job, an MDM-pushed script) cannot click that dialog. It either needs the controlling app pre-approved in the Automation list, or it fails. This is the most common reason a wallpaper script that works in your interactive terminal does nothing when run from a launch agent.
- If you ever denied the prompt by mistake, the toggle to re-enable it lives in that same Automation pane.
Script a random wallpaper from a folder
This is the payoff: point a script at a folder of images and have it pick one at random. It writes to a unique temp path each run so the cache gotcha above never triggers.
#!/usr/bin/env bash
# te-random-wallpaper.sh: set a random desktop wallpaper from a folder.
# Author: Ishan Karunaratne — https://techearl.com/set-mac-wallpaper-command-line
set -euo pipefail
te_random_wallpaper() {
local dir="${1:-$HOME/Pictures/Wallpapers}"
# Pick one image at random:
local pick
pick="$(find "$dir" -type f \( -iname '*.jpg' -o -iname '*.png' -o -iname '*.heic' \) | sort -R | head -n 1)"
if [ -z "$pick" ]; then
echo "No images found in $dir" >&2
return 1
fi
osascript -e "tell application \"System Events\" to set picture of every desktop to \"$pick\""
echo "Wallpaper set to: $pick"
}
te_random_wallpaper "$@"Save it, make it executable with chmod +x te-random-wallpaper.sh, and run it. To rotate the wallpaper on a schedule, wire it into a launchd agent (or cron) and remember the Automation-permission caveat above: the controlling process has to be pre-approved or the osascript call silently does nothing.
If you live in the terminal for this kind of thing, the same osascript pattern drives a lot of macOS appearance from the command line. See the related guides below for toggling dark mode and changing the system volume the same way.
See also
- Toggle dark mode from the macOS command line - the same
osascriptand System Events pattern, applied to the system appearance. - Change the system volume from the macOS command line - another one-line
osascriptcontrol for your Mac. - Open apps, files, and URLs from the macOS command line - the
opencommand, the everyday companion to scripting the desktop.
Sources
Authoritative references this article was fact-checked against.





