Choose the automation
Build a safe dry run
Preview every planned file or application action before allowing the automation to change real state.
A dry run shows what an automation would do, without doing it. Add one before the first destructive operation, while the script is young. It should print the exact source, destination, application command, or deletion it would perform.
The mechanics are one flag and one branch:
#!/bin/zsh
dry_run=0
if [[ "$1" == "--dry-run" ]]; then
dry_run=1
shift
fi
if [[ $dry_run == 1 ]]; then
printf "move %q -> %q\n" "$source" "$destination"
else
mv -- "$source" "$destination"
fi
The %q format matters. It quotes each path the way the shell would need it. A filename with spaces or a stray quote shows up honestly in the preview, instead of looking like two separate files.
Read the preview as questions
Run the preview against real input before every first live run:
./sort-screenshots --dry-run
# move Screenshot\ 2026-08-03.png -> /Users/flavio/Projects/acme/media/acme-2026-08-03.png
Read each printed line as a question. Is this the file I meant? Is it going where I meant? If any line surprises you, you just found a bug for free.
Then feed it the awkward cases: duplicates, spaces, missing input, an unexpected directory. A preview that hides edge cases is not a safety control. If the dry run reports three files and the live run touches forty, the preview lied. And it lied at the worst possible moment, when you trusted it.
Keep the two paths together
The one way this pattern fails is drift. Someone adds a deletion to the live branch and forgets the preview branch. Now the preview is a story about an older script.
Keep the decision logic shared. Branch at the last possible moment, at the single line that changes state. The example above does exactly that. Both branches use the same $source and $destination. Only the final action differs.
If you find yourself writing if dry_run in five places, the script is deciding things inside the branches. Pull the decisions up, and leave only the action down there.
My default
New automations of mine default to dry-run mode. They need an explicit --run flag for their first few weeks. It costs one extra word every time I run them. It has saved me from moving the wrong folder more than once.
You’ll be surprised how often the preview catches something you were sure was fine.
Add a --dry-run flag to the script from the previous lesson. Then run it on a folder with one duplicate name and one filename with a space, and check every line it prints.
Lesson completed