Cos'è PowerShell e perché è diverso da tutto il resto
Come funziona PowerShell, perché lavora con oggetti invece che con testo, i cmdlet essenziali e le differenze pratiche rispetto a CMD e Bash.
Chi arriva a PowerShell dal mondo Unix di solito lo giudica male nei primi dieci minuti: i comandi sono lunghi, la sintassi è verbosa e sembra tutto più macchinoso. Poi capisce la cosa che lo rende diverso da ogni altra shell — non lavora con testo — e la verbosità comincia ad avere senso. In questo articolo trovi come funziona, i comandi che servono davvero e cosa cambia rispetto a Bash.
Cos'è
PowerShell è la shell e il linguaggio di scripting di Microsoft, e la sua caratteristica distintiva è che i comandi si scambiano oggetti strutturati invece che righe di testo.
Sembra un dettaglio tecnico. È invece la differenza che determina come si scrive tutto il resto.
Due precisazioni utili prima di andare avanti:
PowerShell non è CMD. Il vecchio prompt dei comandi di Windows è un'altra cosa, molto più limitata. Se conosci solo quello, il contesto è in CMD, terminale e PowerShell.
PowerShell funziona anche su Linux e macOS. Dalla versione 7 (quella attuale, chiamata semplicemente PowerShell, mentre la 5.1 preinstallata su Windows si chiama Windows PowerShell) è multipiattaforma e open source.
La differenza che cambia tutto: gli oggetti
In Bash, ogni comando produce testo e il comando successivo deve ritagliarlo per estrarre quello che gli serve. È il motivo per cui strumenti come grep, awk, sed e cut sono indispensabili: servono a interpretare testo.
Il classico "quali processi consumano più memoria" su Unix richiede di ritagliare colonne:
ps aux | sort -nrk 4 | head -5 | awk '{print $11, $4}'
Funziona, ma dipende dalla posizione delle colonne: cambia il formato dell'output e lo script si rompe.
In PowerShell:
Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 5 Name, WorkingSet
Non c'è nessun ritaglio di testo. Get-Process restituisce oggetti processo con proprietà vere, e i comandi successivi lavorano su quelle proprietà per nome. Se cambia la formattazione a schermo, lo script continua a funzionare, perché non dipendeva dalla formattazione.
Questo è il vantaggio strutturale di PowerShell, ed è anche la ragione della verbosità: i nomi lunghi sono il prezzo di un modello in cui non devi indovinare in quale colonna stia il dato.
La sintassi dei comandi
I comandi si chiamano cmdlet e seguono sempre lo schema Verbo-Sostantivo:
Get-Process
Stop-Service
New-Item
Remove-Item
Set-Location
La convenzione è rigida, ed è il motivo per cui PowerShell si indovina. Se sai che esiste Get-Service, sai che esistono Start-Service, Stop-Service e Restart-Service senza doverlo cercare.
Per scoprire cosa esiste:
Get-Command *service* # cerca cmdlet
Get-Help Get-Process -Full # documentazione completa
Get-Help Get-Process -Examples
Get-Process | Get-Member # QUALI PROPRIETÀ ha l'oggetto
Get-Member è il comando più utile da imparare per primo. Ti dice esattamente quali proprietà e metodi ha quello che stai ricevendo, quindi cosa puoi usare nel comando successivo. Senza, si procede a tentativi.
Gli alias rendono l'uso interattivo più sopportabile:
ls # Get-ChildItem
cd # Set-Location
cat # Get-Content
rm # Remove-Item
ps # Get-Process
Attenzione però: negli script conviene scrivere i nomi completi. Gli alias possono cambiare o non esistere su un'altra macchina, e chi legge lo script un anno dopo ti ringrazia.
I comandi che userai
Sui file:
Get-ChildItem -Recurse -Filter *.log # elenca ricorsivamente
Get-Content log.txt -Tail 20 # ultime 20 righe
Get-Content log.txt -Wait # segui in tempo reale
Copy-Item origine.txt destinazione.txt
Remove-Item vecchio.txt
New-Item -ItemType Directory nuova-cartella
Test-Path C:\percorso # esiste?
Filtrare e ordinare — lo schema che si ripete ovunque:
Get-ChildItem | Where-Object { $_.Length -gt 1MB }
Get-Process | Where-Object { $_.CPU -gt 100 }
Get-ChildItem | Sort-Object LastWriteTime -Descending
Get-Process | Select-Object Name, CPU -First 10
$_ rappresenta l'oggetto corrente nella pipeline. È l'equivalente concettuale della riga corrente in Bash, ma è un oggetto completo con tutte le sue proprietà.
Servizi e processi:
Get-Service | Where-Object Status -eq Running
Restart-Service -Name Spooler
Stop-Process -Name blocco-notes -Force
Rete:
Test-NetConnection google.com -Port 443
Get-NetIPAddress
Invoke-WebRequest https://api.esempio.it/dati
Dove il modello a oggetti si nota davvero
Esportare in CSV senza scrivere una riga di formattazione:
Get-Process | Select-Object Name, CPU, WorkingSet | Export-Csv processi.csv -NoTypeInformation
Leggere JSON come struttura navigabile, non come testo:
$dati = Get-Content config.json | ConvertFrom-Json
$dati.database.host
Questa è la cosa che in Bash richiede uno strumento esterno e che qui è integrata. Per chi lavora con API e file di configurazione, è il vantaggio più concreto nell'uso quotidiano.
Uno script vero
param(
[Parameter(Mandatory=$true)]
[string]$Cartella,
[int]$GiorniDaTenere = 30
)
$ErrorActionPreference = "Stop"
if (-not (Test-Path $Cartella)) {
Write-Error "La cartella $Cartella non esiste"
exit 1
}
$limite = (Get-Date).AddDays(-$GiorniDaTenere)
$daEliminare = Get-ChildItem -Path $Cartella -Filter *.log |
Where-Object { $_.LastWriteTime -lt $limite }
Write-Host "Trovati $($daEliminare.Count) file da eliminare"
foreach ($file in $daEliminare) {
Write-Host "Elimino $($file.Name)"
Remove-Item $file.FullName
}
Due righe meritano attenzione.
$ErrorActionPreference = "Stop" è l'equivalente di set -e in Bash: senza, molti errori non interrompono lo script, che prosegue in uno stato incoerente. Va messa in cima a ogni script serio.
param() con Mandatory dà validazione automatica dei parametri: se manca, PowerShell lo chiede invece di fallire a metà esecuzione.
Prima di eseguire qualcosa di distruttivo, la rete di sicurezza integrata:
Remove-Item vecchio.txt -WhatIf # mostra cosa farebbe, senza farlo
Remove-Item vecchio.txt -Confirm # chiedi conferma
-WhatIf non ha equivalente diretto in Bash ed è una delle cose migliori di PowerShell: provi il comando distruttivo a vuoto e vedi l'elenco esatto di cosa toccherebbe.
Errori comuni
Gli script non partono per la politica di esecuzione. Errore classico alla prima volta. Windows blocca l'esecuzione degli script per impostazione predefinita:
Get-ExecutionPolicy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
RemoteSigned è il compromesso sensato: i tuoi script girano, quelli scaricati devono essere firmati. Evita Unrestricted: quella protezione esiste per una ragione.
Confondere -eq con =. In PowerShell = assegna, e i confronti si fanno con -eq, -ne, -gt, -lt, -like. Chi arriva da altri linguaggi ci inciampa sempre.
Usare gli alias negli script. Funzionano da te, poi non altrove.
Dimenticare le parentesi in $_.Proprietà dentro una stringa. Serve $($file.Name), non $file.Name: senza le parentesi, la stringa contiene la rappresentazione dell'oggetto seguita da .Name letterale.
Aspettarsi che curl sia curl. In Windows PowerShell è un alias di Invoke-WebRequest, che ha una sintassi completamente diversa. Fonte di confusione ricorrente quando si copiano comandi trovati online.
PowerShell o Bash?
| PowerShell | Bash | |
|---|---|---|
| Modello dei dati | Oggetti | Testo |
| Su Windows | Nativo | Tramite WSL |
| Su Linux/server | Da installare | Ovunque |
| Gestione di Windows | Insuperabile | No |
| Concisione | Verboso | Conciso |
| JSON e API | Integrati | Strumenti esterni |
Non è una scelta da fare una volta per tutte. La linea di demarcazione pratica:
PowerShell per amministrare Windows — Active Directory, servizi, registro, Exchange, Azure. Su quel terreno non ha rivali.
Bash per i server Linux e per gli script che devono girare ovunque. È preinstallata su ogni macchina Linux che incontrerai, e questo pesa più di ogni vantaggio sintattico — vedi Bash scripting.
Se sviluppi su Windows ma pubblichi su Linux, la configurazione più sensata è entrambe: PowerShell per la macchina, WSL per lavorare nello stesso ambiente della produzione.
In sintesi
PowerShell è una shell che scambia oggetti invece che testo, e da questa scelta discende tutto il resto: nomi verbosi ma prevedibili, nessun bisogno di ritagliare colonne, script che non si rompono quando cambia la formattazione.
Le tre cose da imparare per prime sono Get-Member per scoprire cosa hai tra le mani, la coppia Where-Object e Select-Object per filtrare, e -WhatIf per provare a vuoto i comandi distruttivi.
Negli script metti sempre $ErrorActionPreference = "Stop" e i nomi completi dei cmdlet.
È lo strumento giusto per amministrare Windows e per lavorare con JSON e API. Per i server Linux, Bash resta la scelta pragmatica, perché è già lì.