MediumHow I Used AI and Bash to Organize and Declutter My Mac | by Akarsh Seggemu, M.Sc. | Medium
Sitemap

How I Used AI and Bash to Organize and Declutter My Mac

A practical walkthrough of automating Downloads, Documents, and Desktop cleanup with zsh, including the macOS shell gotcha that broke it on the first try

--

In every Mac has three folders: Downloads, Documents, and Desktop. I had a lot of files — meeting recordings, screenshots, PDFs, half-finished project folders, and zip files. I didn’t want to spend time doing the regular tasks of organsizing files into folders by myself. So I asked an AI assistant to help me write a script to do the regular tasks. The process ended up teaching me something interesting about macOS itself.

Press enter or click to view image in full size
Illustration of a messy pile of colorful file icons on the left flowing into a dark terminal window running an organize script, then emerging as neat, color-coded stacked folders on the right, against a dark blue background.

Step 1: Know What You’re Dealing With

Before touching anything, I exported a simple file listing from Terminal:

for d in ~/Downloads ~/Documents ~/Desktop; do
echo "=== $d ==="
find "$d" -maxdepth 2 -type f -exec stat -f "%N | %z bytes | %Sm" {} \;
done > ~/Desktop/file_listing.txt

This gave a full inventory of file paths, sizes, and modification dates without me having to check all the three separate folders. Turning this into numbers was comfortable to review.

You will end with the three folder files sizes.

Step 2: Don’t Touch the Sensitive Stuff

Buried in the mix were files are secret files.

Any organizing script needs a hard rule here: never move, rename, or touch anything that looks like a secret (credential). Not into a “sensitive” folder, not anywhere, just leave it exactly where it is and flag it in a report so a human can deal with it properly (ideally by importing it into a password manager and deleting the original).

The detection logic ended up being a simple pattern match:

if [[ "$ext" == "ovpn" || "$ext" == "pem" || "$ext" == "key" \
|| "$lower_base" == *"password"* || "$lower_base" == *"secret"* \
|| "$lower_base" == *"recovery"* ]]; then
echo "[SKIPPED - sensitive, left in place] '$base'"
continue
fi

No exceptions, just skip and move on.

Step 3: The Bug That Taught Me About macOS’s Ancient Shell

The first version of the script was written in bash, and it used an associative array to count how many files landed in each category:

declare -A category_counts

Ran it on macOS. Instant failure:

./organize_mac_folder.sh: line 73: declare: -A: invalid option

Here’s the thing most people don’t know: macOS still ships bash 3.2 by default — a version from 2007. Apple froze it there over a licensing dispute (bash 4+ switched to GPLv3) and never looked back. Associative arrays didn’t arrive until bash 4.0. So any script written assuming a modern bash will quietly break on a stock Mac.

There’s a second, sneakier bug hiding in old bash too: versions before 4.4 throw false unbound variable errors when you expand an empty array under strict mode (set -u). So even after fixing the associative array, a completely empty folder would crash the script.

Get Akarsh Seggemu, M.Sc.’s stories in your inbox

Join Medium for free to get updates from this writer.

The real fix wasn’t patching around bash 3.2 — it was asking the obvious question: why use bash at all? macOS has defaulted to zsh since Catalina in 2019, and it’s a genuinely modern shell (5.8+) with real associative arrays and none of these landmines. Rewriting the script natively in zsh made the workarounds unnecessary:

typeset -A category_counts   # just works

Lesson: when you’re scripting for macOS, target the shell macOS actually runs today, not the one it’s contractually stuck shipping.

Step 4: The Sorting Logic

With the right shell in place, the actual organizing logic is refreshingly simple. Two rules:

  1. Loose files at the top level get sorted into category folders by type — Screenshots, PDFs, Images, Office Docs, Code & Configs, Archives, Videos, Misc.
  2. Existing subfolders — the ones I’d already grouped myself, like a project folder or a notes directory — get moved as a whole unit into a Project Folders bucket. Nothing inside them gets reshuffled. The script respects organization I'd already done.

And critically, it defaults to a dry run. Nothing moves until you explicitly pass --apply:

./organize_mac_folder.zsh ~/Downloads          # preview only
./organize_mac_folder.zsh ~/Downloads --apply # actually move files

Every dry run prints exactly what would happen, file by file, so you can sanity-check before committing.

Step 5: Bringing It All Together

Running the script separately on Downloads, Documents, and Desktop leaves you with three different Screenshots folders, three PDFs folders, and so on — better than chaos, but still scattered across three locations.

The last piece was a consolidation script that merges all matching category folders into a single home, ~/Documents/Sorted:

~/Documents/Sorted/Screenshots   ← merged from Downloads, Desktop, Documents
~/Documents/Sorted/PDFs ← merged from all three
~/Documents/Sorted/Project Folders
...

The only tricky part is name collisions, what if Downloads/Screenshots/shot1.png and Desktop/Screenshots/shot1.png both exist? The script handles it automatically by tagging the later file with its source:

Sorted/Screenshots/shot1.png
Sorted/Screenshots/shot1.png_from_Desktop

Nothing gets silently overwritten. Empty folders left behind get cleaned up. Sensitive files, once again, are never part of the equation — they were excluded from the start.

What I Ended Up With

  • One clean, predictable structure in ~/Documents/Sorted, sorted by file type
  • A lot of files that were previously invisible due to clutter are now visible and quantified
  • Secrets are flagged for manual review instead of being auto-filed somewhere risky
  • A script that’s safe to re-run any time new clutter piles up, since it’s idempotent by design

Takeaways if You Want to Try This Yourself

  1. Measure before you organize. A quick find + stat export turns the three folders into actual numbers that you can act on.
  2. Treat secrets as untouchable, not just handle carefully. The safest folder for a password export is no folder but a password manager.
  3. Default to dry-run. Any script that moves files on your behalf should show you the plan before doing anything irreversible.
  4. Know your shell. If you’re scripting for macOS, remember it defaults to zsh, not the bash your muscle memory might reach for and Apple’s bash 3.2 will bite you in ways that look like bugs in your own logic.

The whole thing consist of three scripts,

  1. inventory script
  2. sort script
  3. consolidation script

it took less time to build and test than it would have taken to manually drag and drop files from various folders. And unlike a one time cleanup, it’s something that I can just run again any time.

Have your own Mac clutter horror story? I’d love to hear what’s hiding in your folders.

If you like my articles, please follow me on Medium, you can also watch my videos on YouTube and you can also support me by buying me a coffee.

--

--

Akarsh Seggemu, M.Sc.
Akarsh Seggemu, M.Sc.

Written by Akarsh Seggemu, M.Sc.

DevOps Team Lead | Practical guides on cloud automation, GCP, Kubernetes, Terraform, CI/CD, and developer productivity | M.Sc. Computer Science, TU Berlin