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).
Set the wallpaper on every desktop with AppleScript
If you write AppleScript proper (a .scpt you keep in Script Editor, or a step in a larger script) rather than a one-liner, the canonical form is the same set picture of every desktop statement spelled out as a tell block:
tell application "System Events"
set picture of every desktop to "/Users/you/Pictures/bg.jpg"
end tell
every desktop is what makes this hit all displays and Spaces at once, instead of just the screen you happen to be looking at. To run that block straight from the shell without a saved file, flatten it into a single osascript -e invocation:
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/you/Pictures/bg.jpg"'Two caveats worth knowing. A Space created after you run this can keep the old picture until you recreate it, because macOS stamps the wallpaper onto a Space when it is built. And recent macOS versions are finicky about wallpaper scripting permissions: System Events needs Automation permission for the app driving it (see the permission section below), or the command runs and changes nothing.
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, the same encoding you reach for when you base64-encode or decode a file from the command line), 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.
~, so use an absolute path or let the shell expand $HOME first), or you overwrote the image at a path macOS has already cached. On Sonoma and later, write the new image to a fresh filename, or run killall Dock and killall WallpaperAgent to force a redraw.sudo. Adding sudo would run the command as root, which sets root's wallpaper, not yours.every desktop form sets the same image on all displays at once (one desktop object per display). For a different image per display you have to address each desktop by index in an AppleScript loop, which is fiddly across macOS versions. For per-display control with less pain, a dedicated tool such as desktoppr is more reliable than scripting it by hand.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. - Take a photo with the Mac camera from the command line - capture an image you can then drop straight into the random-wallpaper folder above.
Sources
Authoritative references this article was fact-checked against.





