Script: Automatic Memory Card Import with Terminal (Mac)

The Nerd’s Guide to Lightning-Fast Photo Imports

As a photographer, your time is best spent editing and selecting your best shots, not manually dragging files from your memory card. I wanted to find the fastest and most efficient way to automate the photo import process on Mac, without dealing with Finder, slow transfers, or duplicate images.

The result? A Terminal script that automates the entire workflow:

  • Imports photos from your memory card instantly

  • Organizes files neatly by date

  • Prevents duplicates using hash-based detection

  • Displays a visual progress bar with ETA

  • Opens Lightroom (or another app) when the import is finished

Why Use This Method Over Finder or Photo Mechanic?

There are many ways to import photos, such as Finder, Lightroom, or tools like Photo Mechanic, but this Terminal method has unique advantages:


Speed
Terminal and rsync are much faster than Finder or Photo Mechanic for transferring files.

🤖
Automation
No manual steps; just plug in your memory card, and the script does the rest.

Fewer Errors
No forgotten files, duplicate names, or corrupt transfers.

🔧
Full Control
Decide exactly how and where your files are stored.

💸 FREE!
No need for paid software — just a powerful and reliable script.

This guide will walk you through step-by-step so you can automate your photo import workflow on Mac in just a few minutes! 🚀🔥

Installing the Script

1. Open Terminal (Cmd + Space, type ‘Terminal’, press Enter).

2. Check if rsync is installed:

rsync --version

If you have an older version (2.6.9 or lower), update rsync via Homebrew:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install rsync

3. Create a script file:

nano ~/import_photos.sh

4. Paste the following script into the file:

#!/bin/bash

# 🔥 ASCII Metal Logo 🤘
echo "🔥 WELCOME TO CROP & ROLL IMPORT 🤘🔥"
echo "======================================="
echo "     \m/  \m/  \m/  \m/  \m/  \m/"
echo "======================================="
sleep 1  # Pause for effect 🤘

# ✅ Imported files database for incremental ingest
IMPORTED_FILES_DB="/tmp/imported_files.txt"
touch "$IMPORTED_FILES_DB"  # Ensure the file exists

# ✅ Remember last custom name
LAST_CUSTOM_NAME_FILE="/tmp/last_custom_name.txt"
if [ -f "$LAST_CUSTOM_NAME_FILE" ]; then
    LAST_CUSTOM_NAME=$(cat "$LAST_CUSTOM_NAME_FILE")
else
    LAST_CUSTOM_NAME=""
fi

# ✅ Function: Clean up old imported files (older than 90 days)
clean_old_imports() {
    local temp_file="${IMPORTED_FILES_DB}.tmp"
    local cutoff_date=$(date -v-90d +%Y-%m-%d)

    awk -v cutoff="$cutoff_date" '$1 >= cutoff' "$IMPORTED_FILES_DB" > "$temp_file"
    mv "$temp_file" "$IMPORTED_FILES_DB"
}

# ✅ Function: Check available disk space before import
check_disk_space() {
    AVAILABLE_SPACE=$(df -k "$HOME" | awk 'NR==2 {print $4}')
    AVAILABLE_GB=$((AVAILABLE_SPACE / 1024 / 1024))

    if [[ $AVAILABLE_GB -lt 10 ]]; then
        echo "⚠️ Warning: Only $AVAILABLE_GB GB free on your disk!"
        osascript -e 'display notification "Low disk space! Only '$AVAILABLE_GB'GB left." with title "Photo Import"'
    fi

    if [[ $AVAILABLE_GB -lt 2 ]]; then
        echo "❌ ERROR: Not enough space ($AVAILABLE_GB GB). Import stopped."
        osascript -e 'display notification "Not enough space! Import stopped." with title "Photo Import"'
        exit 1
    fi
}

# ✅ Function: Detect all SD cards
detect_sd_cards() {
    SD_CARDS=()
    for volume in /Volumes/*; do
        if [[ -d "$volume/DCIM" ]]; then
            SD_CARDS+=("$volume/DCIM")
        fi
    done
}

# ✅ Function: Check if a file has already been imported
file_already_imported() {
    local file_hash="$1"
    grep -q "$file_hash" "$IMPORTED_FILES_DB"
}

# ✅ Function: Add file hash to database after successful import
add_file_to_db() {
    local file_hash="$1"
    echo "$(date +%Y-%m-%d) $file_hash" >> "$IMPORTED_FILES_DB"
}

# ✅ Function: Start import of a single SD card
import_sd_card() {
    SD_PATH="$1"
    echo "🚀 Starting import from: $SD_PATH"

    # ✅ Ask for a custom name (suggest last used name)
    echo "📝 Enter a custom name (or press Enter to keep '$LAST_CUSTOM_NAME'):":
    read CUSTOM_NAME
    if [[ -z "$CUSTOM_NAME" ]]; then
        CUSTOM_NAME=$LAST_CUSTOM_NAME
    fi
    echo "$CUSTOM_NAME" > "$LAST_CUSTOM_NAME_FILE"

    check_disk_space
    clean_old_imports

    DEST="$HOME/Pictures/RAW"
    mkdir -p "$DEST"

    for file in "$SD_PATH"/*; do
        if [[ -f "$file" ]]; then
            file_hash=$(md5 -q "$file")  # Compute hash

            # ✅ Check if file has already been imported
            if file_already_imported "$file_hash"; then
                echo "⚠️ Skipping: $(basename "$file") (Already imported)"
                continue
            fi

            photo_date=$(stat -f "%Sm" -t "%Y-%m-%d" "$file")
            photo_time=$(stat -f "%Sm" -t "%H%M" "$file")
            basename=$(basename "$file")

            photo_dest="$DEST/$photo_date"
            mkdir -p "$photo_dest"

            newname="${photo_date}_${photo_time}_${CUSTOM_NAME}_$(basename "$file")"
            /opt/homebrew/bin/rsync -a "$file" "$photo_dest/$newname"
            add_file_to_db "$file_hash"
        fi
    done

    echo -e "\n✅ Import complete! 🎸🔥"
    echo "💿 Ejecting SD card..."
    diskutil unmountDisk "$SD_PATH"
}

# ✅ Main loop to allow multiple card imports
while true; do
    detect_sd_cards
    for SD_PATH in "${SD_CARDS[@]}"; do
        import_sd_card "$SD_PATH"
    done

    # ✅ Ask user if they want to import another card
    echo "🔄 Do you want to import another memory card? (y/n)"
    read answer
    if [[ "$answer" != "y" ]]; then
        break
    fi
    echo "📢 Please insert the next memory card and press Enter."
    read
    sleep 2
done

# ✅ Launch Lightroom only after all cards are done!
echo "🚀 All cards have been imported! Launching Lightroom..."
open -a "Adobe Lightroom Classic"

echo "✅ Script completed and exited!"
exit 0

Key Features and Customization

Changing the Destination Folder

By default, photos are saved in:

DEST="$HOME/Pictures/RAW"

To change this, modify the path. Example for an external drive:

DEST="/Volumes/ExternalDrive/Photography/RAW"

If you use cloud storage (e.g., OneDrive, Dropbox, Google Drive), you can change it like this:

DEST="$HOME/Library/CloudStorage/OneDrive/Photography/RAW"

💡 Make sure the destination folder exists, or the script will create it automatically.

Skipping Duplicate Files

The script prevents duplicate imports by checking file hashes (unique digital fingerprints for each file). This means renaming a file doesn’t trick the system, and exact duplicates will be skipped.

✅ How it works: Before importing a file, the script calculates its MD5 hash and checks if it’s already in the import history (imported_files.txt). If a file with the same hash exists, it is skipped.

✅ Code snippet from the script:

file_already_imported() {
    local file_hash="$1"
    grep -q "$file_hash" "$IMPORTED_FILES_DB"
}

✅ If you want to reset the history and re-import everything: Run this command before your next import:

rm /tmp/imported_files.txt

💡 The script automatically removes old import records older than 90 days, so you don’t need to manually clean up.

Changing the App That Opens After Import

The script opens Lightroom by default:

open -a "Adobe Lightroom Classic"

To open Capture One, change it to:

open -a "Capture One"

Or remove the line entirely if you don't want any app to open.

Adjusting the Import Naming Structure

By default, your files are renamed as follows:

YYYY-MM-DD_HHMM_CustomName_OriginalFilename

Example:

2025-02-06_1530_Concert-Metallica_IMG_4567.CR3

If you want to change the structure, modify this line in the script:

newname="${photo_date}_${photo_time}_${CUSTOM_NAME}_$(basename "$file")"

For example, if you want to include the photographer’s name first:

newname="Wouter-Vellekoop_${photo_date}_${photo_time}_${CUSTOM_NAME}_$(basename "$file")"



Making the Script Run

Running the Script Manually

Whenever you insert a memory card, open Terminal and run:

~/import_photos.sh


This will start the import process immediately.

Create a Terminal Alias

Create a shortcut by adding this line to ~/.zshrc:

alias import="~/import_photos.sh"

Create a Mac App with Automator

  1. Open Automator and choose Application.

  2. Add a Shell Script action.

  3. Paste:

~/import_photos.sh

4. Save as, and you got your own application!

Conclusion:

With this script, importing photos on Mac becomes a seamless and fully automated process. No more manually dragging and dropping files, no more duplicate images filling up your storage, and no more waiting around—just plug in your memory card, and let the script handle everything. 🚀

By leveraging rsync for high-speed transfers, hash-based duplicate detection, and a structured folder organization, this workflow ensures that every import is fast, reliable, and optimized for photographers. Whether you shoot concerts, events, or portraits, this script will streamline your Mac photography workflow and eliminate unnecessary steps.

Key features that make this the best way to import photos on Mac:

Fastest memory card import method using rsync

Prevents duplicate images with hash-based verification

Automates file renaming and organization

Customizable progress bar and import settings

Works with Lightroom, Capture One, or any editing software

This script is ideal for professional photographers looking for the fastest way to import and organize photos on Mac. Whether you’re dealing with hundreds or thousands of RAW files, it ensures a non-destructive, efficient, and fully automated workflow.

💡 Want to make this even better? If you have questions, feature requests, or feedback, feel free to reach out! I’d love to hear how this script is improving your workflow and what features you’d like to see next. 🚀🔥

Previous
Previous

The Ultimate Guide to Concert Photography