Sotto la scocca di alluminio c'è un Unix vero (Darwin, XNU), ma con regole di casa molto più severe di Linux. APFS, Homebrew, launchd, Gatekeeper e tutto quello che serve a chi lo amministra sul serio, non solo lo usa.
"Perché sed -i non funziona?" — la prima domanda che si pone ogni sysadmin Linux davanti a un Mac, di solito seguita da una ricerca febbrile su BSD vs GNU userland.
Non è "un Linux con un tema grafico bello": è un albero genealogico Unix diverso, con un kernel diverso e un rapporto diverso col concetto di "root può fare tutto".
Darwin è il sistema operativo open source (licenza Apple Public Source License) su cui macOS è costruito: il kernel XNU (eredità di NeXTSTEP, l'azienda di Steve Jobs prima di tornare in Apple), più un userland derivato da FreeBSD. Sopra Darwin, Apple aggiunge gli strati chiusi che rendono macOS quello che conosci: Aqua (l'interfaccia grafica), Cocoa (i framework per le app), e tutto il resto che non trovi nel codice sorgente pubblico.
Se Linux è un appartamento condiviso dove puoi riverniciare i muri e spostare i mobili come vuoi (root può letteralmente tutto), macOS è un appartamento con lo stesso impianto idraulico Unix di base, ma con un padrone di casa (Apple) che ha messo lucchetti su certe stanze anche per l'inquilino con le chiavi di tutte le porte (root). Vedrai questi lucchetti per tutta la guida: si chiamano SIP (cap. 8), TCC (cap. 9), sandboxing delle app.
| Aspetto | Linux | macOS |
|---|---|---|
| Kernel | Monolitico modulare (vedi la guida Kernel Linux) | XNU: ibrido Mach (microkernel) + BSD |
| PID 1 / init | systemd (quasi ovunque oggi) | launchd, dal 2005 — concettualmente il precursore dell'idea |
| Filesystem | ext4/XFS/Btrfs (vedi Gestione Dischi) | APFS (cap. 3), da High Sierra 2017 |
| Gestione pacchetti nativa | APT/dpkg, dnf/rpm | App Store + Homebrew (non ufficiale ma standard de facto, cap. 4) |
| Userland | GNU coreutils | BSD coreutils (comandi con opzioni diverse, cap. 5) |
| root può tutto? | Sì, letteralmente | No: SIP (cap. 8) blocca anche root su file di sistema |
Da Catalina (2019) in poi, il disco di un Mac non è più "un filesystem": sono due volumi separati che si comportano come se fossero uno solo.
Dalla versione Catalina, il sistema operativo vive su un Signed System Volume (SSV): un volume APFS crittograficamente firmato e di sola lettura, montato separatamente dal volume Data dove vivono le tue app, i tuoi dati utente e le configurazioni scrivibili. I due volumi appaiono fusi in un'unica vista tramite firmlink (non link simbolici: un meccanismo APFS dedicato), così /Applications sembra un'unica cartella anche se in realtà parte del suo contenuto vive fisicamente sul volume Data.
diskutil apfs list
diskutil list
# Cerca "Volume Group" e i volumi "Macintosh HD" (System, read-only)
# e "Macintosh HD - Data" (scrivibile) nello stesso container APFS
| Percorso | Cosa contiene |
|---|---|
/System | Il sistema operativo vero e proprio, sola lettura, firmato |
/Library | Risorse condivise per tutti gli utenti (preferenze di sistema, driver, LaunchDaemons) |
/Users/<utente>/Library | Dati e preferenze specifiche dell'utente (nascosta di default nel Finder) |
/Applications | App installate a livello di sistema |
/private/etc | Configurazioni di sistema in stile Unix (link simbolico da /etc) |
/private/var | Log, cache, dati variabili (link simbolico da /var) |
/opt | Storicamente vuota su macOS; Homebrew su Apple Silicon la usa (cap. 4) |
/System: oltre al fatto che è montato read-only, SIP (cap. 8) lo protegge comunque anche se tecnicamente riuscissi a rimontarlo scrivibile. Qualsiasi personalizzazione seria del sistema passa da /Library o dal profilo utente, non dal volume System.
Apple File System, introdotto nel 2017, è il motivo per cui certe operazioni su Mac sono istantanee mentre su altri sistemi richiederebbero minuti.
APFS non copia mai davvero un file finché non viene modificato: un cp di un file di 50GB è istantaneo, perché crea solo un nuovo riferimento agli stessi blocchi fisici. Solo quando uno dei due file cambia, APFS scrive fisicamente solo i blocchi diversi (copy-on-write, lo stesso principio di Btrfs/ZFS visto nella guida Gestione Dischi).
Un container APFS può ospitare più volumi (System, Data, Recovery, VM per lo swap...) che condividono lo stesso spazio libero invece di avere dimensioni fisse come le partizioni tradizionali. Non devi decidere in anticipo "quanto spazio per il sistema, quanto per i dati": crescono entrambi finché il container ha spazio.
| Feature | APFS | HFS+ (il vecchio standard, fino a High Sierra) |
|---|---|---|
| Copy-on-write | Sì | No |
| Snapshot nativi | Sì (usati da Time Machine, cap. 11) | No |
| Cifratura | Nativa, per volume o per file | Solo per intero volume (FileVault 1/2) |
| Ottimizzato per | SSD/flash | Dischi rotanti (progettato nel 1998) |
| Case-sensitive di default? | No (ma esiste la variante case-sensitive) | No |
tmutil localsnapshot # crea uno snapshot locale adesso
tmutil listlocalsnapshots /
tmutil deletelocalsnapshots <data>
Non ufficiale, non di Apple, eppure lo standard de facto per chiunque amministri un Mac da riga di comando.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
/opt/homebrew; sui vecchi Mac Intel, in /usr/local. Molti problemi "Homebrew non trova il comando" dopo l'installazione sono semplicemente il PATH non aggiornato per il percorso giusto — lo script di installazione stampa il comando eval $(/opt/homebrew/bin/brew shellenv) da aggiungere al tuo .zprofile: eseguilo, non ignorarlo.
wget, git, postgresql)brew install --cask visual-studio-code), scaricano il .dmg/.pkg ufficiale e lo installano al posto tuobrew services start postgresql
brew services list
brew services stop postgresql
brew search nginx
brew info nginx
brew install nginx
brew update && brew upgrade
brew cleanup # rimuove versioni vecchie scaricate
brew doctor # diagnostica problemi di installazione
brew list --versions
La sintassi sembra identica a Linux finché non lo è più. Il colpevole si chiama BSD userland.
Da Catalina in poi, zsh ha sostituito bash come shell di default (per ragioni di licenza: bash su macOS è fermo alla vecchia versione 3.2 GPLv2, Apple non può aggiornarlo senza passare a GPLv3). File di configurazione: ~/.zshrc (interattiva), ~/.zprofile (login, dove Homebrew mette lo shellenv), ~/.zshenv (sempre, anche in script non interattivi).
| Comando | Su Linux (GNU) | Su macOS (BSD) |
|---|---|---|
| sed in-place | sed -i 's/x/y/' file | sed -i '' 's/x/y/' file (serve l'argomento vuoto per il backup) |
| date da timestamp | date -d @123456 | date -r 123456 |
| readlink assoluto | readlink -f file | Non supportato allo stesso modo (serve greadlink o Python) |
| ls colori | ls --color | ls -G |
g, senza sovrascrivere quelli di sistema.
brew install coreutils gnu-sed grep findutils → poi gsed, ggrep, gfind si comportano esattamente come su Linux. Se scrivi script che devono girare identici su entrambi i sistemi, questa è la soluzione più pulita.
Non esiste un registro unico come su Windows: le preferenze vivono in file .plist sparsi, e c'è un comando dedicato per leggerli e scriverli.
Ogni app e ogni componente di sistema salva le proprie preferenze in un file property list (.plist, XML o binario), identificato dal bundle ID dell'app (es. com.apple.finder.plist). Le preferenze utente vivono in ~/Library/Preferences/, quelle di sistema in /Library/Preferences/.
# Leggere una preferenza
defaults read com.apple.finder AppleShowAllFiles
# Scriverla: mostra i file nascosti nel Finder
defaults write com.apple.finder AppleShowAllFiles -bool true
killall Finder # l'app deve riavviarsi per notare il cambiamento
# Convertire un plist binario in XML leggibile
plutil -convert xml1 -o - ~/Library/Preferences/com.apple.finder.plist
defaults write, se non vedi l'effetto, prova a riavviare l'app (killall NomeApp) o, per preferenze più profonde, l'intero demone di sistema cfprefsd con killall cfprefsd.
Apple ha sostituito init con un service manager moderno nel 2005, anni prima che Linux facesse lo stesso passaggio a systemd.
| Tipo | Dove vive | Gira come |
|---|---|---|
| LaunchDaemon | /Library/LaunchDaemons/ | root, prima ancora del login utente |
| LaunchAgent (sistema) | /Library/LaunchAgents/ | Per ogni utente che fa login |
| LaunchAgent (utente) | ~/Library/LaunchAgents/ | Solo per l'utente proprietario |
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.esempio.backup</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/mio-script-backup.sh</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.esempio.backup.plist
launchctl list | grep com.esempio.backup
launchctl bootout gui/$(id -u)/com.esempio.backup
launchctl load/unload sono deprecati dal Big Sur: usa bootstrap/bootout con il dominio esplicito (gui/$(id -u) per un agent utente, system per un daemon). La sintassi vecchia spesso "sembra funzionare" ma dà risultati inconsistenti sulle versioni moderne.
Tre meccanismi distinti che insieme decidono cosa può girare sul Mac, e cosa nemmeno root può toccare.
Dalla versione El Capitan (2015), SIP protegge file e cartelle di sistema (/System, /usr tranne /usr/local, le app preinstallate) da modifiche anche da parte di un processo root. È il motivo per cui certi malware storici (che sostituivano binari di sistema) semplicemente non funzionano più.
csrutil status
csrutil disable, richiede il riavvio in Recovery Mode) toglie una protezione fondamentale del sistema. Va fatto solo per sviluppo/debug avanzato specifico, mai come soluzione a un problema che non capisci, e mai su una macchina di produzione o aziendale.
Gatekeeper controlla, al primo avvio di un'app, che sia firmata con un certificato sviluppatore valido e (per le app distribuite fuori dall'App Store) notarizzata — cioè scansionata dai server Apple per malware noto prima della distribuzione. Il messaggio "non può essere aperta perché proviene da uno sviluppatore non identificato" è Gatekeeper al lavoro.
spctl --status
spctl -a -v /Applications/App.app
xattr -l /Applications/App.app | grep quarantine
com.apple.quarantine: è quello che triggera il controllo Gatekeeper. Se sei certo della provenienza di un binario (es. compilato da te stesso e trasferito via rete), puoi rimuoverlo con xattr -d com.apple.quarantine /path/app — ma è esattamente il controllo che Gatekeeper esiste per far rispettare, quindi usalo con consapevolezza, non come "il comando magico per far sparire l'avviso".
Il motivo per cui il tuo script di automazione perfettamente innocuo non riesce a leggere un file, finché non dai il permesso esplicito a Terminal stesso.
Dal Mojave (2018), macOS richiede il consenso esplicito dell'utente, app per app, per accedere a risorse sensibili: fotocamera, microfono, posizione, contatti, e soprattutto Accessibilità, Registrazione schermo e Accesso completo al disco (Full Disk Access). Questi permessi vivono in Impostazioni di Sistema → Privacy e sicurezza, ma sono gestibili anche da CLI per scenari di IT/automazione.
# Reset di TUTTI i permessi concessi a un'app (utile in test/debug)
tccutil reset All com.esempio.miaapp
# Reset di una singola categoria di permesso per tutte le app
tccutil reset Camera
~/Desktop, ~/Documents o dischi esterni può fallire silenziosamente (o con errori di permesso fuorvianti) se Terminal stesso non ha ricevuto Accesso completo al disco in Privacy e sicurezza. Non è un bug del tuo script: è TCC che blocca l'app "genitore" dell'automazione, non solo lo script in sé.
Cifratura full-disk con le chiavi custodite in hardware dedicato, non solo in software.
Sui Mac con chip Apple Silicon (o T2 sugli ultimi Intel), FileVault si appoggia al Secure Enclave: un coprocessore isolato che gestisce le chiavi crittografiche senza mai esporle al sistema operativo principale. Anche smontando fisicamente il disco SSD (che spesso è saldato sulla scheda, altro dettaglio da IT), i dati restano illeggibili senza il Secure Enclave della macchina originale.
fdesetup status
sudo fdesetup enable
# Genera una chiave di recovery personale, o usa una institutional key (MDM, cap. 12)
Un utente singolo ottiene una chiave di recovery personale (o la lega al proprio Apple ID). In contesti aziendali gestiti via MDM (cap. 12), è comune impostare una chiave istituzionale condivisa, così l'IT può sbloccare un disco anche se l'utente ha dimenticato la propria password.
Legato a "Dov'è" (Find My) e all'Apple ID del proprietario, impedisce che un Mac rubato o perso venga riutilizzato senza le credenziali originali — rilevante per l'IT quando gestisce dismissioni o resi di hardware aziendale: va disattivato correttamente prima, altrimenti il dispositivo resta bloccato per il prossimo utilizzatore.
Lo stesso motore di snapshot APFS visto al cap. 3, esteso a un disco esterno o di rete per uno storico che va oltre l'ultima ora.
Time Machine combina gli snapshot locali APFS (automatici, ogni ora, finché c'è spazio) con backup incrementali su un disco esterno o di rete: solo i blocchi cambiati da un backup all'altro vengono copiati davvero, mantenendo comunque l'illusione di "copie complete" a ogni punto nel tempo grazie ai riferimenti condivisi (lo stesso principio copy-on-write del cap. 3).
tmutil startbackup
tmutil status
tmutil listbackups
tmutil addexclusion ~/Downloads/cache-pesante
tmutil thinlocalsnapshots / 5000000000 4 # libera spazio se serve
Come si amministrano decine o migliaia di Mac senza toccarli fisicamente uno per uno.
Apple Business Manager (ABM, aziende) e Apple School Manager (istituti scolastici) permettono di legare i dispositivi acquistati direttamente a un programma di Automated Device Enrollment: un Mac nuovo, appena acceso, si registra da solo a un server MDM aziendale durante il setup iniziale, senza intervento manuale.
| Soluzione MDM | Nota |
|---|---|
| Jamf Pro | Lo standard enterprise più diffuso specificamente per il mondo Apple |
| Kandji / Mosyle | Alternative moderne, spesso più economiche per PMI |
| Microsoft Intune | Se l'azienda è già nell'ecosistema Microsoft 365 e vuole un'unica console multi-piattaforma |
| Apple Configurator | Gratuito, per configurazione manuale/locale di pochi dispositivi via cavo |
profiles show -type enrollment
profiles list
profiles show -type configuration
.mobileconfig) sono il meccanismo con cui un MDM distribuisce impostazioni Wi-Fi, VPN, restrizioni, certificati e policy di sicurezza a distanza: concettualmente equivalgono ai file .deb/pacchetti di configurazione visti nella guida Pacchetti .deb, ma per la gestione centralizzata di dispositivi invece che di software.
Tutto quello che serve, su una pagina. Bookmark questa sezione e dimentica il resto.
log show --last 1h --predicate 'eventMessage contains "errore"'
log stream --predicate 'process == "nomeapp"'
top -o cpu
ps aux | grep processo
diskutil list
diskutil apfs list
diskutil verifyVolume /
diskutil repairVolume /
Cmd+R all'avvio (Intel) o il tasto d'accensione (Apple Silicon)Shift all'avvio: carica solo le estensioni essenzialiCmd+Opt+P+R all'avvio (solo Intel; su Apple Silicon è automatico)Cmd+V all'avvio, mostra i log testuali invece del logocsrutil status
spctl --status
fdesetup status
tccutil reset All com.esempio.app
com.apple.quarantine da binari di provenienza incertalaunchctl load/unload deprecato invece di bootstrap/bootoutsed -i su macOS richiede l'argomento di backup esplicito