To sort top-level Downloads files into extension folders, use a script that examines immediate files, creates folders such as txt and csv, and skips existing destination names. Start with the disposable example below, preview its moves, then run it from Terminal or Apple Shortcuts.

This walkthrough is for a Mac user who wants to physically move loose files into folders while leaving existing subfolders and their contents alone. Its main decision aid is a preview report: every eligible file gets a proposed destination, while collisions and unsupported names get an explicit skip message.

What will move, and what will stay in place?

Finder offers sorting and grouping controls, along with folders and Smart Folders. Apple explains that a Smart Folder presents matching files together without moving them. For this task, the desired result is actual extension folders containing moved files. Organize your files in the Finder on Mac.

Before inspecting the example, open Finder and choose Finder > Settings > Advanced, then select Show all filename extensions. This makes the suffixes used by the sorter visible. Showing an extension does not change the file’s format. Show or hide filename extensions on Mac.

The script below uses these deliberate rules. They describe this implementation, rather than universal Finder or Shortcuts behavior:

Item encountered Sorter behavior
An ordinary immediate file such as note.TXT Proposes txt/note.TXT; preserves the original filename
A compound name such as bundle.tar.gz Uses the final suffix: gz/bundle.tar.gz
A file without a dot, or ending in a dot Leaves it in place and reports a skip
A folder, including one named with an extension Leaves it and its contents in place
A name beginning with a dot Excludes it from the scan
A symbolic link Leaves it in place
An extension containing anything except ASCII letters and digits, or longer than 32 characters Leaves it in place and reports a skip
An existing destination with the same filename Leaves the source in place and reports a collision
A destination folder name occupied by a regular file or symbolic link Reports a blocked folder and leaves the source in place

The extension restrictions keep the folder-naming rule simple and predictable. A skipped file remains available for a manual decision; the script does not invent a replacement name or silently discard it.

Create a disposable Downloads example

Open Terminal and paste the following complete setup block. It creates only synthetic text files inside Downloads/ExtensionSortDemo. Some have illustrative extensions such as .gz; their contents are plain sample text, not examples of those formats. The setup stops if that demo folder already exists, so rerunning it will not reset an earlier test.

Apple documents Terminal as a way to execute commands and shell scripts, and documents mv for moving local files. Here, Terminal provides a readable way to preview each planned move before making it. What is Terminal on Mac?, Manage files in Terminal on Mac.

/bin/bash <<'BASH'
set -eu
demo="$HOME/Downloads/ExtensionSortDemo"
/bin/mkdir "$demo"
/bin/mkdir "$demo/nested" "$demo/txt"
printf 'sample text\n' > "$demo/sample.txt"
printf 'uppercase suffix example\n' > "$demo/note.TXT"
printf 'name,value\nblue,1\n' > "$demo/palette.csv"
printf 'synthetic compound-name example\n' > "$demo/bundle.tar.gz"
printf 'no extension example\n' > "$demo/README"
printf 'trailing dot example\n' > "$demo/trailing."
printf 'hidden example\n' > "$demo/.hidden.txt"
printf 'nested example\n' > "$demo/nested/inside.txt"
printf 'existing destination example\n' > "$demo/txt/sample.txt"
printf 'blocks creation of a csv folder\n' > "$demo/csv"
printf 'Created demo folder: %s\n' "$demo"
BASH

Open Downloads in Finder, then open ExtensionSortDemo. Notice the two different files named sample.txt: one at the demo’s top level and one already inside txt. Also notice that csv is a file. These are intentional tests of two common reasons a move should be skipped.

Preview the sorter before moving files

Paste the complete program below into Terminal. Its initial setting is mode=preview, so it reports proposed moves without creating extension folders or moving files. The configured root is the disposable demo folder, not the whole Downloads folder.

/bin/bash <<'BASH'
set -u
export LC_ALL=C
unset GLOBIGNORE
shopt -s nullglob
shopt -u dotglob

root="$HOME/Downloads/ExtensionSortDemo"
mode=preview

case "$mode" in
  preview|move) ;;
  *) printf 'Invalid mode\n' >&2; exit 1 ;;
esac
if [ ! -d "$root" ] || [ -L "$root" ]; then
  printf 'Root must be an existing ordinary folder\n' >&2
  exit 1
fi
for file in "$root"/*; do
  if [ -L "$file" ] || [ ! -f "$file" ]; then
    printf 'SKIP non-regular: %q\n' "$file"
    continue
  fi
  name=${file##*/}
  case "$name" in
    *.*) ext=${name##*.} ;;
    *) printf 'SKIP no extension: %q\n' "$name"; continue ;;
  esac
  case "$ext" in
    ''|*[!A-Za-z0-9]*)
      printf 'SKIP unsupported extension: %q\n' "$name"
      continue ;;
  esac
  if [ "${#ext}" -gt 32 ]; then
    printf 'SKIP long extension: %q\n' "$name"
    continue
  fi
  ext=$(printf '%s' "$ext" | /usr/bin/tr '[:upper:]' '[:lower:]')
  folder="$root/$ext"
  dest="$folder/$name"
  if [ -L "$folder" ] || { [ -e "$folder" ] && [ ! -d "$folder" ]; }; then
    printf 'SKIP blocked folder: %q\n' "$folder"
    continue
  fi
  if [ -e "$dest" ] || [ -L "$dest" ]; then
    printf 'SKIP collision: %q\n' "$dest"
    continue
  fi
  if [ "$mode" = preview ]; then
    printf 'WOULD MOVE %q -> %q\n' "$file" "$dest"
    continue
  fi
  /bin/mkdir -p "$folder" || exit 1
  /bin/mv -n "$file" "$dest" || exit 1
  if [ -e "$file" ] || [ -L "$file" ]; then
    printf 'NOT MOVED: %q\n' "$file" >&2
    exit 1
  fi
  printf 'MOVED %q -> %q\n' "$file" "$dest"
done
printf 'Finished (%s). Review all SKIP lines.\n' "$mode"
BASH

The immediate-child pattern "$root"/* does not descend into directories. The file checks then exclude directories and symbolic links. nullglob handles an empty folder without treating an unmatched pattern as a filename; disabling dotglob excludes dot-prefixed names. These behaviors are documented in Apple’s distributed Bash manual. Bash manual.

The script extracts the final suffix and converts its letters to lowercase with tr. Consequently, note.TXT keeps its filename but goes into txt. Apple’s tr manual documents the uppercase-to-lowercase character mapping used here. tr(1) — translate characters.

For this fixture, the important expected results are:

Synthetic source Expected preview result
note.TXT Would move to txt/note.TXT
bundle.tar.gz Would move to gz/bundle.tar.gz
sample.txt Collision with txt/sample.txt; skip
palette.csv Folder name csv is blocked by a file; skip
README No extension; skip
trailing. Empty extension; skip
nested/inside.txt Never examined
.hidden.txt Never examined

These are expected filesystem results, not a claim that the moves were executed during preparation. The program passed a Bash syntax check, and a separate deterministic filename-mapping check confirmed suffix extraction and lowercase conversion. This preparation environment was read-only, so the complete macOS move workflow was not executed.

Move the example files and check the result

Once the preview matches the table, paste the same complete program again with only mode=preview changed to mode=move. Keep the demo root unchanged for this test.

In move mode, mkdir -p creates the required folder or accepts an existing directory. It does not turn an existing ordinary file into a folder, which is why the script checks the destination folder first. mkdir(1) — make directories.

The move uses macOS mv -n, whose documented behavior is to avoid overwriting an existing file. The earlier existence check provides a readable collision report; the subsequent source check catches a move that left the source behind. The program stops on a reported command failure. mv(1) — move files.

Check the demo in Finder. You should find note.TXT inside txt, and bundle.tar.gz inside a newly created gz folder. Both versions of sample.txt should still exist in their original locations. palette.csv should remain at the top level, and nested/inside.txt should remain inside nested.

The final Finished line means the scan completed; review its skip messages as well. Running the sorter again should leave the already sorted files inside their folders because those folders are not scanned recursively.

Run the same sorter from Apple Shortcuts

Shortcuts can serve as a button for this script. Apple documents built-in support for writing and running shell scripts in the Shortcuts editor. The shell program performs the extension mapping and existence checks; this article does not assume that a native file-moving action has identical collision behavior. Meet Shortcuts for macOS.

Create the shortcut manually and keep it pointed at the demo while testing:

  1. Open Shortcuts, click the add button, and name the shortcut Sort Extension Demo.
  2. Search the action list for Run Shell Script and add that action.
  3. Set its shell to /bin/bash. Paste the complete sorter block above, including its opening /bin/bash line and final BASH delimiter, into the script field. Keep mode=preview initially.
  4. Add Quick Look after the script action and connect it to the script’s output.
  5. Run the shortcut and inspect the preview report before changing the script to mode=move.

Apple documents creating a shortcut by adding actions and testing it, and recommends Quick Look for inspecting content passed between actions. Create a custom shortcut on Mac, Testing your actions in Shortcuts on Mac.

This shortcut requires no incoming files: the root path is defined inside the script. Its successful output is a text report, which Quick Look displays. That report is not a collection of moved file objects to feed into another file action. To inspect an action’s accepted input and resulting output on your installed version, Control-click its icon and choose Show Info, as Apple documents. Navigate the action list in Shortcuts on Mac.

If Shortcuts requires script execution to be enabled, Apple’s documented setting is Shortcuts > Settings > Advanced > Allow Running Scripts. If the shell action is unavailable on your installation, use the complete Terminal program above; it performs the same sorting task independently. Advanced Privacy and security settings in Shortcuts on Mac.

Apply the rule to top-level Downloads

After the disposable test, the configuration change for the actual Downloads folder is the following single-line replacement inside the sorter. This is a configuration fragment, not a standalone sorting program:

root="$HOME/Downloads"

Return to mode=preview whenever you change the root. Review every proposed destination and skip message before enabling moves. The program processes eligible immediate files together; it does not offer an individual selection prompt.

Use it as a manually started batch on a folder whose files have finished arriving. The script does not determine download completion, monitor future additions, or coordinate concurrent runs. Its checks and move are separate operations, so keep other file-moving activity out of the folder while it runs. A preview is a report of the current state, not a reservation of those destinations.

For a collision, inspect the two files in Finder and decide whether to rename the top-level file or leave it where it is. For a blocked extension folder, resolve the item occupying that folder name before rerunning. Neither condition calls for changing the program to overwrite files.

Finish by checking the actual destination folders in Finder and reviewing any remaining top-level files against the report. The disposable example gives you a concrete acceptance test: the intended files move, existing names remain intact, and nested contents stay where they started.

Sources