Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

File Operations and Manipulation

File operations on macOS have unique considerations: extended attributes, resource forks, ACLs, and macOS-specific tools like ditto. This chapter covers practical file operations while handling macOS-specific metadata correctly.

cp: Copying Files

Basic Operations (Mostly Compatible)

# Copy file (compatible)
$ cp source.txt dest.txt

# Copy directory recursively (compatible)
$ cp -R source_dir/ dest_dir/

# Preserve permissions and timestamps (compatible)
$ cp -p source.txt dest.txt

# Interactive mode - prompt before overwrite (compatible)
$ cp -i source.txt dest.txt

# Verbose output (compatible)
$ cp -v source.txt dest.txt

macOS-Specific Considerations

# Preserve extended attributes and resource forks
$ cp -p source.txt dest.txt    # -p includes xattrs on macOS

# Explicitly preserve all metadata
$ cp -a source_dir/ dest_dir/   # Same as -pPR (preserves everything)

# Don't follow symbolic links (compatible)
$ cp -P symlink dest/

# Copy without extended attributes (macOS specific)
# There's no direct flag; use ditto or strip after

Progress Indicator

# GNU cp has --progress (not available on macOS)
$ cp --progress large_file.iso /dest/    # Linux
cp: illegal option -- -                   # macOS

# macOS alternatives:

# 1. Use rsync with progress
$ rsync --progress large_file.iso /dest/

# 2. Use pv (pipe viewer, install via brew)
$ brew install pv
$ pv source.iso > dest/source.iso

# 3. Use macOS cp with Ctrl+T for status
$ cp large_file.iso /dest/
# Press Ctrl+T during copy to see progress
load: 2.45  cmd: cp 12345 uninterruptible 0.00u 1.23s
source.iso -> /dest/source.iso  45%

Handling Conflicts

# Don't overwrite existing (compatible)
$ cp -n source.txt dest.txt

# Force overwrite (compatible)
$ cp -f source.txt dest.txt

# Update only if source is newer - GNU only
$ cp -u source.txt dest.txt

# macOS alternative for update behavior
$ rsync -u source.txt dest.txt

mv: Moving and Renaming

Basic Operations (Compatible)

# Move file
$ mv source.txt /new/location/

# Rename file
$ mv oldname.txt newname.txt

# Move directory
$ mv source_dir/ /new/location/

# Interactive mode
$ mv -i source.txt dest.txt

# Don't overwrite existing
$ mv -n source.txt dest.txt

# Force overwrite
$ mv -f source.txt dest.txt

# Verbose
$ mv -v source.txt dest.txt

Update Mode (GNU only)

# GNU mv - only if source is newer
$ mv -u source.txt dest.txt

# macOS alternative
$ rsync -u --remove-source-files source.txt dest.txt

Moving Across Volumes

When moving files between volumes, mv copies then deletes (can’t just rename):

# Moving between volumes preserves metadata on macOS
$ mv file.txt /Volumes/ExternalDisk/

# For large moves, rsync gives progress
$ rsync -av --progress --remove-source-files source/ /Volumes/External/dest/

rm: Removing Files

Basic Operations (Compatible)

# Remove file
$ rm file.txt

# Remove directory and contents
$ rm -r directory/

# Force remove (no prompts)
$ rm -f file.txt

# Interactive mode
$ rm -i file.txt

# Verbose
$ rm -v file.txt

Secure Delete

# Secure delete - macOS (deprecated but still works)
$ rm -P file.txt    # 3-pass overwrite before delete

# Note: On SSDs with TRIM, secure delete is less effective
# FileVault encryption is more reliable for security

Safe Remove Practices

# Trash instead of delete (use trash-cli)
$ brew install trash-cli
$ trash file.txt    # Moves to Trash instead of deleting

# Or use AppleScript
$ osascript -e 'tell app "Finder" to delete POSIX file "'$(pwd)/file.txt'"'

ditto: macOS’s Superior Copy Tool

ditto is Apple’s copy utility, designed for macOS-specific needs:

# Basic copy (preserves everything)
$ ditto source.txt dest.txt
$ ditto source_dir/ dest_dir/

# Copy with verbose output
$ ditto -V source_dir/ dest_dir/

# Preserve extended attributes and ACLs (default)
$ ditto source/ dest/

# Flatten to tar archive
$ ditto -c -k --sequesterRsrc source_dir/ archive.zip

# Extract from archive
$ ditto -x -k archive.zip dest_dir/

Why Use ditto Over cp?

# ditto advantages:
# 1. Properly handles resource forks
# 2. Preserves HFS+ metadata
# 3. Can create/extract ZIP archives
# 4. Works correctly with bundles (apps)

# Copy an application bundle
$ ditto /Applications/TextEdit.app ~/Desktop/TextEdit.app

# cp -r might miss resource forks or metadata
# ditto is the safer choice for macOS files

Creating Archives with ditto

# Create ZIP archive
$ ditto -c -k --sequesterRsrc --keepParent source_dir/ archive.zip

# Options:
#   -c : create archive
#   -k : use PKZip format
#   --sequesterRsrc : store resource forks in __MACOSX
#   --keepParent : include parent directory in archive

# Create archive without __MACOSX metadata
$ ditto -c -k --norsrc source_dir/ archive.zip

rsync: Synchronization

macOS includes rsync, but it’s an older version. The Homebrew version has more features:

# Check version
$ rsync --version
rsync  version 2.6.9  protocol version 29    # System version (old)

# Install newer version
$ brew install rsync
$ /opt/homebrew/bin/rsync --version
rsync  version 3.2.7  protocol version 31    # Much newer

Basic Synchronization

# Sync directories
$ rsync -av source/ dest/

# Options breakdown:
#   -a : archive mode (preserves permissions, timestamps, etc.)
#   -v : verbose

# With progress
$ rsync -av --progress source/ dest/

# Dry run (show what would happen)
$ rsync -av --dry-run source/ dest/

# Delete files in dest that aren't in source
$ rsync -av --delete source/ dest/

Preserving macOS Metadata

# System rsync doesn't have extended attribute support
# Homebrew rsync does

# Preserve extended attributes
$ rsync -avX source/ dest/    # -X for extended attributes

# Full macOS preservation (Homebrew rsync 3.x)
$ rsync -av --xattrs --fileflags source/ dest/

# For cross-platform transfers, you might want to strip metadata
$ rsync -av --no-xattrs source/ dest/

Remote Synchronization

# Copy to remote (SSH)
$ rsync -av source/ user@remote:/path/to/dest/

# Copy from remote
$ rsync -av user@remote:/path/to/source/ dest/

# With compression for slow links
$ rsync -avz source/ user@remote:/dest/

# Limit bandwidth (KB/s)
$ rsync -av --bwlimit=1000 source/ user@remote:/dest/

# Resume interrupted transfer
$ rsync -av --partial --progress source/ dest/

# Combine for reliable large transfers
$ rsync -avz --partial --progress --bwlimit=5000 source/ user@remote:/dest/

Excluding Files

# Exclude patterns
$ rsync -av --exclude='*.log' --exclude='.DS_Store' source/ dest/

# Exclude from file
$ rsync -av --exclude-from='exclude.txt' source/ dest/

# Common exclusions for macOS
$ rsync -av \
    --exclude='.DS_Store' \
    --exclude='._*' \
    --exclude='.Spotlight-*' \
    --exclude='.Trashes' \
    --exclude='.fseventsd' \
    source/ dest/

Extended Attributes (xattr)

macOS stores additional metadata in extended attributes:

# List extended attributes
$ xattr file.txt
com.apple.quarantine

# View attribute content
$ xattr -p com.apple.quarantine file.txt
0083;5f3e8bc0;Chrome;XXXXXXXX

# Remove quarantine attribute (common need for downloaded files)
$ xattr -d com.apple.quarantine file.txt

# Remove all extended attributes
$ xattr -c file.txt

# Recursive operations
$ xattr -r -d com.apple.quarantine app_folder/

# Copy preserving xattrs
$ cp -p file.txt dest.txt    # -p preserves xattrs

# List with details
$ xattr -l file.txt
com.apple.quarantine: 0083;5f3e8bc0;Chrome;XXXXXXXX
com.apple.lastuseddate#PS: ... (binary data)

Common Extended Attributes

AttributePurpose
com.apple.quarantineDownloaded file, triggers Gatekeeper
com.apple.lastuseddate#PSLast opened date
com.apple.metadata:kMDItemWhereFromsDownload URL
com.apple.FinderInfoFinder metadata (labels, etc.)
com.apple.ResourceForkClassic resource fork data

Stripping Metadata for Transfer

# Remove all macOS metadata before sharing
$ xattr -cr directory/

# Or use tar with no extended attributes
$ COPYFILE_DISABLE=1 tar cvf archive.tar directory/

# Copy without metadata using rsync
$ rsync -av --no-xattrs source/ dest/

find: Finding Files

Basic Usage (Mostly Compatible)

# Find by name (compatible)
$ find . -name "*.txt"

# Find by type (compatible)
$ find . -type f    # Files
$ find . -type d    # Directories

# Find by modification time (compatible)
$ find . -mtime -7    # Modified in last 7 days

# Find by size (compatible)
$ find . -size +100M    # Larger than 100MB

# Execute command on results (compatible)
$ find . -name "*.tmp" -exec rm {} \;

# Delete found files (compatible)
$ find . -name "*.tmp" -delete

macOS-Specific Options

# Extended regex (BSD find)
$ find -E . -regex ".*\.(jpg|png|gif)"

# GNU find uses -regextype
$ find . -regextype posix-extended -regex ".*\.(jpg|png|gif)"

# Find with extended attributes
$ find . -xattrname com.apple.quarantine

# Find files by Spotlight metadata
$ mdfind -onlyin . "kMDItemFSSize > 1000000"

Handling Spaces in Filenames

# Using -print0 and xargs -0 (compatible)
$ find . -name "*.txt" -print0 | xargs -0 rm

# Using -exec (compatible)
$ find . -name "*.txt" -exec rm {} \;

mkdir: Creating Directories

# Create directory (compatible)
$ mkdir newdir

# Create parent directories as needed (compatible)
$ mkdir -p path/to/new/dir

# Set permissions on creation (compatible)
$ mkdir -m 755 newdir

# Verbose (compatible)
$ mkdir -v newdir
# Create hard link (compatible)
$ ln original.txt hardlink.txt

# Create symbolic link (compatible)
$ ln -s /path/to/original symlink

# Force overwrite existing link (compatible)
$ ln -sf /new/target symlink

# Create relative symbolic link
$ ln -s ../sibling/file.txt symlink
# macOS symbolic links work across volumes
$ ln -s /Volumes/External/file.txt local_link

# Aliases vs Symbolic Links
# Finder aliases are NOT symbolic links
# They track file moves; symlinks don't

# Create Finder alias from command line
$ osascript -e 'tell application "Finder" to make alias file to POSIX file "/path/to/original" at POSIX file "/path/to/location"'

chmod, chown: Permissions

Basic Usage (Compatible)

# Change permissions (compatible)
$ chmod 755 script.sh
$ chmod u+x script.sh
$ chmod -R 644 directory/

# Change owner (compatible)
$ sudo chown user:group file.txt
$ sudo chown -R user:group directory/

macOS Specific

# macOS also has ACLs
$ ls -le file.txt    # Shows ACL entries

# Remove ACLs
$ chmod -N file.txt

# View with full permissions
$ ls -l@ file.txt    # Shows extended attributes

Touch: Create/Update Timestamps

# Create empty file or update timestamp (compatible)
$ touch file.txt

# Set specific modification time (BSD syntax)
$ touch -t 202401151200 file.txt    # YYYYMMDDhhmm

# Use another file's timestamp (compatible)
$ touch -r reference.txt target.txt

# Only update if file exists (compatible)
$ touch -c file.txt

Practical Examples

Backup a Directory Preserving Metadata

# Best method on macOS
$ ditto -V source_dir/ backup_dir/

# Or with rsync
$ rsync -av --progress source_dir/ backup_dir/

Clean Up .DS_Store Files

# Find and delete
$ find . -name ".DS_Store" -delete

# Also remove ._* AppleDouble files
$ find . -name "._*" -delete

# Prevent .DS_Store on network volumes
$ defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true

Remove Quarantine from Downloads

# Single file
$ xattr -d com.apple.quarantine download.dmg

# Entire directory
$ xattr -dr com.apple.quarantine ~/Downloads/

# From an app
$ xattr -cr /Applications/SomeApp.app

Sync with External Drive

# Initial sync
$ rsync -av --progress ~/Documents/ /Volumes/Backup/Documents/

# Update (only changed files)
$ rsync -av --progress --delete ~/Documents/ /Volumes/Backup/Documents/

Find Large Files

# Using find
$ find ~ -type f -size +100M 2>/dev/null

# Using Spotlight (faster)
$ mdfind -onlyin ~ "kMDItemFSSize > 104857600"

# Sorted by size
$ find ~ -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -h

Summary: macOS File Operations

TaskBest CommandNotes
Copy filesditto or cp -pditto preserves everything
Sync directoriesrsync -avUse Homebrew rsync for xattr support
Large file copyrsync --progressShows progress
Move to Trashtrash (brew)Safer than rm
Remove quarantinexattr -d com.apple.quarantineCommon need
Archive directoryditto -c -kCreates ZIP with metadata
Strip metadataxattr -crBefore sharing
Find filesmdfindUses Spotlight, much faster

Understanding these tools and their macOS-specific behaviors helps you manage files effectively while preserving the metadata that macOS applications expect.