Skip to content

Instantly share code, notes, and snippets.

@samelie
Created November 8, 2025 17:00
Show Gist options
  • Select an option

  • Save samelie/af485df79f8034f834767724407986b2 to your computer and use it in GitHub Desktop.

Select an option

Save samelie/af485df79f8034f834767724407986b2 to your computer and use it in GitHub Desktop.
Format external drives for Raspberry Pi and macOS compatibility
#!/bin/bash
##############################################################################
# format-drive.sh - Format external drives for Raspberry Pi and macOS
# Runs from macOS, formats drive with compatibility for both platforms
##############################################################################
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Defaults
FILESYSTEM="ext4"
MOUNT_POINT="/mnt/external"
FORCE_FORMAT=false
INTERACTIVE=true
DRY_RUN=false
##############################################################################
# Helper Functions
##############################################################################
log_info() {
echo -e "${BLUE}ℹ${NC} $1"
}
log_success() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
usage() {
cat << EOF
Usage: $0 [OPTIONS]
Format external drives for Raspberry Pi and macOS compatibility.
OPTIONS:
-d, --device DEVICE Device path (e.g., /dev/disk4)
-f, --filesystem FS Filesystem type: ext4, exfat, apfs (default: ext4)
-m, --mount-point PATH Mount point on Pi (default: /mnt/external)
-F, --force Force format without confirmation
--dry-run Show what would be done, don't execute
-h, --help Show this help message
EXAMPLES:
# Interactive mode - will prompt for device
$0
# Format /dev/disk4 to ext4
$0 -d /dev/disk4 -f ext4
# Force format to exfat (macOS compatible)
$0 -d /dev/disk4 -f exfat -F
# Dry run to see what would happen
$0 -d /dev/disk4 --dry-run
EOF
exit 0
}
list_drives() {
log_info "Available external drives:"
diskutil list external
}
confirm_action() {
local prompt="$1"
local response
if [ "$FORCE_FORMAT" = true ]; then
return 0
fi
read -p "$(echo -e ${YELLOW}?)${NC} $prompt (yes/no): " response
if [[ "$response" =~ ^[Yy][Ee]?[Ss]?$ ]]; then
return 0
else
return 1
fi
}
##############################################################################
# macOS-specific Functions
##############################################################################
get_device_info() {
local device="$1"
if [ ! -e "$device" ]; then
log_error "Device not found: $device"
return 1
fi
log_info "Device information for $device:"
diskutil info "$device"
}
unmount_device() {
local device="$1"
log_info "Unmounting $device..."
# Get all partitions for this device
local partitions
partitions=$(diskutil list "$device" | grep -E "^\s+[0-9]+" | awk '{print $NF}' || true)
if [ -z "$partitions" ]; then
log_warn "No partitions found to unmount"
return 0
fi
for partition in $partitions; do
if diskutil info "/dev/$partition" &>/dev/null; then
local mount_point
mount_point=$(diskutil info "/dev/$partition" | grep "Mount Point:" | awk '{$1=$2=""; print $0}' | xargs || true)
if [ -n "$mount_point" ] && [ "$mount_point" != "Not mounted" ]; then
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would unmount: /dev/$partition from $mount_point"
else
diskutil unmount "/dev/$partition" || true
fi
fi
fi
done
log_success "Unmount complete"
}
eject_device() {
local device="$1"
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would eject: $device"
else
diskutil eject "$device" || log_warn "Could not eject device (may still be in use)"
fi
}
##############################################################################
# Partitioning & Formatting
##############################################################################
partition_drive() {
local device="$1"
log_info "Creating GPT partition table on $device..."
log_info "Using exFAT as intermediate (ext4 not supported natively on macOS)"
log_warn "You'll need to format to ext4 on Raspberry Pi if needed"
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would run: diskutil partitionDisk $device GPT exFAT PIEXT4 0b"
return 0
fi
# macOS doesn't support ext4 natively, use exFAT as workaround
diskutil partitionDisk "$device" GPT "exFAT" "PIEXT4" 0b
}
partition_drive_exfat() {
local device="$1"
log_info "Creating GPT partition table with exFAT on $device..."
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would run: diskutil partitionDisk $device GPT exFAT PIEXFAT 0b"
return 0
fi
diskutil partitionDisk "$device" GPT "exFAT" "PIEXFAT" 0b
}
partition_drive_apfs() {
local device="$1"
log_info "Creating APFS partition on $device..."
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would run: diskutil partitionDisk $device APFS PIAPFS 0b"
return 0
fi
diskutil partitionDisk "$device" APFS "PIAPFS" 0b
}
##############################################################################
# Main Execution
##############################################################################
main() {
# Parse arguments
while [ $# -gt 0 ]; do
case "$1" in
-d|--device)
DEVICE="$2"
shift 2
;;
-f|--filesystem)
FILESYSTEM="$2"
shift 2
;;
-m|--mount-point)
MOUNT_POINT="$2"
shift 2
;;
-F|--force)
FORCE_FORMAT=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
-h|--help)
usage
;;
*)
log_error "Unknown option: $1"
usage
;;
esac
done
# Check if running on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
log_error "This script is designed to run on macOS"
exit 1
fi
# Check for required tools
if ! command -v diskutil &> /dev/null; then
log_error "diskutil not found - this script requires macOS"
exit 1
fi
log_info "=== External Drive Formatter for Raspberry Pi & macOS ==="
echo ""
# List drives if not specified
if [ -z "${DEVICE:-}" ]; then
list_drives
echo ""
read -p "Enter device path (e.g., /dev/disk4): " DEVICE
fi
# Validate device
if [ ! -e "$DEVICE" ]; then
log_error "Device not found: $DEVICE"
exit 1
fi
# Show device info
get_device_info "$DEVICE"
echo ""
# Validate filesystem
case "$FILESYSTEM" in
ext4|exfat|apfs)
;;
*)
log_error "Unsupported filesystem: $FILESYSTEM"
log_info "Supported: ext4, exfat, apfs"
exit 1
;;
esac
# Show filesystem info
log_info "Requested filesystem: $FILESYSTEM"
if [ "$FILESYSTEM" = "ext4" ]; then
log_warn "Note: macOS can't format ext4 natively"
log_info "Will format as exFAT on macOS, reformat on Raspberry Pi if needed"
elif [ "$FILESYSTEM" = "exfat" ]; then
log_info "Best for: Cross-platform (macOS + Raspberry Pi)"
log_warn "Slightly slower than native formats, no Unix permissions"
elif [ "$FILESYSTEM" = "apfs" ]; then
log_info "Best for: macOS only"
log_error "Not recommended - Raspberry Pi has limited APFS support"
fi
echo ""
# Confirm action
if ! confirm_action "Format $DEVICE with $FILESYSTEM?"; then
log_warn "Cancelled"
exit 0
fi
if [ "$DRY_RUN" = false ]; then
log_warn "This will erase all data on $DEVICE!"
if ! confirm_action "Are you absolutely sure?"; then
log_warn "Cancelled"
exit 0
fi
fi
echo ""
log_info "Starting format process..."
# Unmount device
unmount_device "$DEVICE"
echo ""
# Format based on filesystem
case "$FILESYSTEM" in
ext4)
partition_drive "$DEVICE"
;;
exfat)
partition_drive_exfat "$DEVICE"
;;
apfs)
partition_drive_apfs "$DEVICE"
;;
esac
if [ "$DRY_RUN" = false ]; then
sleep 2
log_success "Format complete!"
echo ""
log_info "Drive info after format:"
diskutil list "$DEVICE"
echo ""
# Get partition device
local partition_device
partition_device=$(diskutil list "$DEVICE" | grep -E "^\s+0:" | awk '{print $NF}')
if [ -n "$partition_device" ]; then
log_info "Partition created: /dev/$partition_device"
log_info "To use on Raspberry Pi, copy this to your ansible playbook:"
log_warn "ssd_device: \"/dev/sda\""
log_warn "mount_point: \"$MOUNT_POINT\""
log_warn "filesystem: \"$FILESYSTEM\""
fi
else
log_success "[DRY RUN] Format would complete successfully"
fi
echo ""
log_success "Done!"
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment