Operate the automation

Make the work idempotent

Design an automation so running it twice reaches the same correct state without duplicating or corrupting work.

10 minute lesson

~~~

An automation is idempotent when running it twice reaches the same correct state as running it once. This matters more for automations than for hand work, because automations rerun: schedules fire again, a Quick Action gets clicked twice, launchd restarts a job that died mid-write.

An idempotent file workflow checks whether the desired output already exists and whether it matches the input it processed. It does not append the date again on every retry, turning one screenshot into acme-2026-08-03-2026-08-03.png on the second pass.

Three techniques do most of the work: stable destination names, temporary files, and an atomic final move.

name="acme-$(date +%F).png"
destination="$HOME/Projects/acme/media/$name"

if [[ -e "$destination" ]]; then
  echo "already processed: $name"
  exit 0
fi

tmp=$(mktemp "$destination.XXXXXX")
cp "$HOME/Desktop/screenshot.png" "$tmp"
mv "$tmp" "$destination"

The destination name is stable: computed from the input, not from “now, plus a counter”. Running twice computes the same name, hits the existence check, and exits cleanly.

The temporary file plus mv is the atomic part. Within one volume, mv is a rename: any other process sees either no file or the complete file, never a half-copied one. If the job dies during cp, the destination never existed, and the next run starts over safely. Record processed input only after the output is complete — here, the mv itself is that record, because the destination’s existence is the marker.

Verify by doing what reality will do to you: run the same fixture twice.

./sort-screenshots && ./sort-screenshots
# moved acme-2026-08-03.png
# already processed: acme-2026-08-03.png

The second run should report no change or the same verified result, not another copy.

The failure mode to hunt for is a marker written before the work finishes. If the script logs “done”, or records the input as processed, and then crashes before the mv, every future run skips a file that was never actually produced. Markers come last.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →