2025-01-30 13:38:00 -07:00
|
|
|
#!/usr/bin/env python3
|
2025-01-30 14:22:51 -07:00
|
|
|
# Description: Easily write ISO images to USB sticks on macOS via CLI.
|
2025-01-30 13:38:00 -07:00
|
|
|
# Usage: python3 iso2usb-mac.py
|
|
|
|
|
# Author: Justin Oros
|
|
|
|
|
# Source: https://github.com/JustinOros
|
|
|
|
|
# Dependencies: dd, diskutil (standard utils on macOS)
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
def list_files(extension):
|
|
|
|
|
# List files in the current directory with the ISO extension.
|
|
|
|
|
return [f for f in os.listdir() if f.endswith(extension)]
|
|
|
|
|
|
|
|
|
|
def list_external_disks():
|
|
|
|
|
# List external disks (USB drives) currently mounted on macOS.
|
|
|
|
|
result = subprocess.run(['diskutil', 'list'], capture_output=True, text=True)
|
|
|
|
|
disks = []
|
|
|
|
|
for line in result.stdout.splitlines():
|
|
|
|
|
if "/dev/disk" in line and "external" in line:
|
|
|
|
|
parts = line.split()
|
|
|
|
|
disks.append(parts[0])
|
|
|
|
|
return disks
|
|
|
|
|
|
|
|
|
|
def prompt_choice(options, prompt):
|
|
|
|
|
# Prompt user to select an option from a list.
|
|
|
|
|
while True:
|
|
|
|
|
for idx, option in enumerate(options, 1):
|
|
|
|
|
print(f"{idx}. {option}")
|
|
|
|
|
try:
|
|
|
|
|
choice = int(input(f"{prompt} (Enter the number): "))
|
|
|
|
|
if 1 <= choice <= len(options):
|
|
|
|
|
return options[choice - 1]
|
|
|
|
|
else:
|
|
|
|
|
print(f"Please enter a number between 1 and {len(options)}.")
|
|
|
|
|
except ValueError:
|
|
|
|
|
print("Invalid input. Please enter a number.")
|
|
|
|
|
|
|
|
|
|
def prompt_speed_choice():
|
|
|
|
|
# Prompt the user to select the write speed.
|
|
|
|
|
print("\nSelect the write speed:")
|
|
|
|
|
print("1) Slow (1M)")
|
|
|
|
|
print("2) Regular (4M) [default]")
|
|
|
|
|
print("3) Fast (8M)")
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
choice = input("Enter the number (or press RETURN for default): ").strip()
|
|
|
|
|
|
|
|
|
|
if choice == "":
|
|
|
|
|
return "4M" # Default to regular speed (4M)
|
|
|
|
|
elif choice == "1":
|
|
|
|
|
return "1M" # Slow speed
|
|
|
|
|
elif choice == "2":
|
|
|
|
|
return "4M" # Regular speed
|
|
|
|
|
elif choice == "3":
|
|
|
|
|
return "8M" # Fast speed
|
|
|
|
|
else:
|
|
|
|
|
print("Invalid input. Please enter 1, 2, or 3, or press RETURN for default.")
|
|
|
|
|
|
|
|
|
|
def confirm(prompt):
|
|
|
|
|
# Prompt user to confirm an action.
|
|
|
|
|
while True:
|
|
|
|
|
confirm = input(f"{prompt} (y/n): ").strip().lower()
|
|
|
|
|
if confirm in ['y', 'n']:
|
|
|
|
|
return confirm == 'y'
|
|
|
|
|
print("Please enter 'y' or 'n'.")
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
# List ISOs in the current directory
|
|
|
|
|
iso_files = list_files('.iso')
|
|
|
|
|
if not iso_files:
|
|
|
|
|
print("No ISO files found in the current directory.")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
# List available external disks
|
|
|
|
|
disks = list_external_disks()
|
|
|
|
|
if not disks:
|
|
|
|
|
print("No external disks found.")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
# Prompt user to select ISO
|
|
|
|
|
selected_iso = prompt_choice(iso_files, "Select an ISO file")
|
|
|
|
|
|
|
|
|
|
# Prompt user to select disk
|
|
|
|
|
selected_disk = prompt_choice(disks, "Select a disk")
|
|
|
|
|
|
|
|
|
|
# Prompt user to select write speed
|
|
|
|
|
selected_speed = prompt_speed_choice()
|
|
|
|
|
|
|
|
|
|
# Overview of selected ISO, disk, and write speed
|
|
|
|
|
print(f"\nYou selected:")
|
|
|
|
|
print(f"ISO: {selected_iso}")
|
|
|
|
|
print(f"Disk: {selected_disk}")
|
|
|
|
|
print(f"Write speed: {selected_speed}")
|
|
|
|
|
|
|
|
|
|
# Confirm before proceeding
|
|
|
|
|
if confirm("Are you sure you want to proceed? This will overwrite the disk"):
|
|
|
|
|
print("Writing ISO to disk...")
|
|
|
|
|
|
|
|
|
|
# Execute dd command to write the ISO to the selected disk with the selected block size
|
|
|
|
|
try:
|
|
|
|
|
# WARNING: dd command will overwrite the disk, be sure!
|
|
|
|
|
subprocess.run(['sudo', 'dd', f'if={selected_iso}', f'of={selected_disk}', 'bs=' + selected_speed, 'status=progress', 'oflag=sync'], check=True)
|
|
|
|
|
|
|
|
|
|
# After writing the ISO, eject the disk to make it ready for use
|
|
|
|
|
subprocess.run(['sudo', 'diskutil', 'eject', selected_disk], check=True)
|
|
|
|
|
|
|
|
|
|
print("ISO successfully written to disk.")
|
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
|
print(f"Error while writing to disk: {e}")
|
|
|
|
|
else:
|
|
|
|
|
print("Operation cancelled.")
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|