Windows PowerShellLa scatola delle palline da ping-pong

Don Jones

Indice

Impersonare il cmdlet
La funzione di filtro
Pensare in termini di oggetti fisici
Applicazioni pratiche
Trovare il tempo per giocare

In una delle ultime lezioni su Windows PowerShell che ho tenuto, alcuni degli studenti hanno avuto serie difficoltà a raffigurarsi la pipeline della shell. Riconosco che il concetto di pipeline non è del tutto intuitivo, pertanto può risultare alquanto difficile immaginare cosa faccia esattamente la pipeline. Quando sono passato al

concetto di funzioni di filtro, che operano direttamente nella pipeline, le cose sono peggiorate e ho potuto leggere lo smarrimento totale nei visi degli studenti.

Per aiutarli, il giorno dopo ho mostrato una scatola, alcune etichette adesive e alcune palline da ping-pong (nessuno può dire che Windows PowerShell® non è divertente). Avevo deciso di fare una dimostrazione usando la riga di comando seguente:

Get-Process | Where { $_.VM –gt 1000 } | Sort VM
–descending | Export-CSV C:\Procs.csv

Impersonare il cmdlet

Usando etichette adesive da attaccare agli abiti, ho assegnato un nome cmdlet a ogni studente. Ho spiegato che le palline da ping-pong rappresentavano oggetti processo di Windows® (più nello specifico, oggetti di tipo System.Diagnostics.Process), quindi ho chiesto agli studenti di indicarmi quale nome cmdlet suonava come il più adatto a generare oggetti processo.

Guardandosi intorno, hanno concordato sulla scelta ovvia di Get-Process. Questo spiega una delle ragioni principali per cui apprezzo i nomi cmdlet usati in Windows PowerShell: sono davvero molto intuitivi.

Così lo studente che rappresentava Get-Process ha afferrato tutte le palline da ping-pong e le ha tirate nella scatola di cartone. La scatola rappresentava la pipeline della shell e quello che hanno fatto gli studenti non è stato altro che quello che fa un cmdlet. Un cmdlet genera uno o più oggetti e poi li lancia nella pipeline.

Il prossimo cmdlet nella pipeline prende il comando. In questo esempio, lo studente che rappresentava il cmdlet Where-Object ha preso tutte le palline da ping-pong e le ha esaminate tutte, una alla volta, per vedere se la proprietà VM di una pallina era maggiore di 1.000. Nella shell, "VM" indica la memoria virtuale e per questo esercizio avevo scritto col pennarello le varie quantità di memoria virtuale su ogni pallina.

Ogni pallina (altrimenti detta processo) che aveva una VM maggiore o uguale a 1.000 tornava nella scatola (talvolta chiamata pipeline), mentre tutte quelle con un valore inferiore venivano buttate definitivamente nel cestino (in realtà non è vero perché le ho recuperate per usarle in altre lezioni).

Quindi, lo studente che rappresentava Sort-Object ha messo in ordine le palline da ping-pong nella scatola in base alla proprietà VM. Devo ammettere che questa parte dell'esercizio era quella meno studiata: tenere ferme le palline è stato alquanto difficile! Sembra che dovrò trovare palline da ping-pong di forma cubica per la prossima lezione o quei pratici cartoni per le uova.

Infine, lo studente che rappresentava Export-CSV ha preso le palline e ha scritto le informazioni in un file CSV. In pratica, ciò si è tradotto nello scrivere le proprietà del processo su una lavagna a fogli che abbiamo fatto finta fosse un file CSV.

La funzione di filtro

Dopo aver visto una semplice pipeline, abbiamo deciso di esaminare le funzioni di filtro che sono un poco più complicate. Ho proposto la funzione di filtro e la riga di comando mostrate nella Figura 1.

Figura 1 Esempio di funzione di filtro e riga di comando

Function Do-Something {
 BEGIN { }
 PROCESS {
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty "TimeStamp" (Get-Date)
  $obj | Add-Member NoteProperty "ProcessName" ($_.Name)
  Write-Output $obj
 }
 END {}
}
Get-Process | Do-Something | Format-Table *

Innanzitutto voglio dare un rapido sguardo a cosa ci si aspetta che facciano le funzioni di filtro. L'idea di fondo è che la funzione contenga tre blocchi di script, chiamati BEGIN, PROCESS e END.

Quando la funzione viene usata nella pipeline, viene eseguito per primo il blocco di script BEGIN. Lo script potrebbe fare qualsiasi attività di impostazione come, ad esempio, aprire una connessione con il database.

Quindi viene eseguito il blocco di script PROCESS una volta per ogni oggetto che era stato inserito nella pipeline della funzione. Nel blocco di script PROCESS, la variabile $_ viene automaticamente riempita con l'oggetto della pipeline corrente. Pertanto, se nella pipeline ci sono 10 oggetti, il blocco di script PROCESS viene eseguito 10 volte.

Infine, dopo che sono stati eseguiti tutti gli oggetti nella pipeline, viene eseguito il blocco di script END che compie tutte le attività di pulizia necessarie, come la chiusura della connessione con un database.

All'interno di questi blocchi di script, tutto quello che viene scritto usando Write-Output si risolve nella pipeline per il prossimo cmdlet. Tutto quello che non è scritto usando Write-Output viene eliminato.

Poiché il nostro comando cominciava con Get-Process, uno degli studenti ha raccolto tutte le palline da ping-pong (abbiamo fatto una breve pausa e in qualche modo le palline sono state allegramente lanciate per tutta la stanza, vedere la figura) e le ha fatte cadere nella scatola della pipeline. Terminato il suo compito, è toccato allo studente che rappresentava la funzione di filtro "Do-Something".

Prima ha eseguito il blocco di script BEGIN. Essendo vuoto, non ha richiesto molta fatica. Quindi ha afferrato tutti gli oggetti della pipeline (le palline da ping-pong) e ha iniziato a guardarli una alla volta. È stato divertente, perché c'erano circa una dozzina di palline e lui cercava di tenerle tutte in grembo!

Ad ogni modo, ha preso un oggetto processo alla volta e ha eseguito il relativo blocco di script PROCESS. Questo blocco di script gli ha fatto creare un oggetto del tutto nuovo (a questo scopo ho usato speciali palline da ping-pong gialle, che possono essere reperite in un negozio di articoli sportivi). Su queste nuove palline, lo studente ha scritto la data e l'ora corrente insieme al nome dell'oggetto processo che stava esaminando al momento.

Questo nuovo oggetto personalizzato è stato scritto poi nella pipeline, cioè lo studente ha messo la pallina da ping-pong gialla nella scatola ed è stata eliminata la pallina dell'oggetto processo originale. Lo studente ha fatto questa operazione per ogni pallina da ping-pong che stava nella scatola, circa 12 in totale. Al termine, ogni pallina da ping-pong bianca è stata sostituita da una pallina gialla relativa a un oggetto personalizzato. Infine, lo studente ha eseguito il proprio blocco di script END che, essendo vuoto, non ha richiesto nemmeno un minuto.

Per concludere, è toccato alla studentessa "Format-Table". Ha preso tutto il contenuto della scatola, che a questo punto erano tutti gli oggetti "personalizzati" gialli, e ha iniziato a costruire una tabella usando entrambe le proprietà scritte su ogni palla. Il risultato è stato una tabella, scritta sulla nostra lavagna a fogli, con due colonne, TimeStamp e ProcessName, e una dozzina circa di righe.

Pensare in termini di oggetti fisici

Grazie a questo esercizio, la pipeline e le funzioni di filtro sono diventate familiari a tutta la classe. Le palline da ping-pong hanno avuto un ruolo decisivo nel rappresentare gli oggetti, ovvero qualcosa che tende a essere astratta quando si parla semplicemente di shell.

Ognuno ha potuto vedere come i cmdlet manipolavano questi oggetti, come i risultati venivano inseriti nella pipeline e come il cmdlet successivo prendeva questi risultati e manipolava ulteriormente gli oggetti. La sequenza di eventi in una funzione di filtro è sembrata più ovvia, come lo è stata la tecnica del blocco di script PROCESS di lavorare con un oggetto di input alla volta.

Ciò ha mostrato anche i mezzi con cui è possibile creare un nuovo oggetto personalizzato e inserirlo nella pipeline. Inoltre, ha aiutato a evidenziare i vantaggi di produrre oggetti personalizzati invece di semplice testo: altri cmdlet, come Format-Table, hanno potuto elaborare i nuovi oggetti personalizzati, mostrando una grande flessibilità nel modo in cui è stato possibile usare e manipolare i dati.

Applicazioni pratiche

La domanda ovvia posta dagli studenti era come fosse possibile usare la pipeline e queste funzioni di filtro in applicazioni pratiche. Per me è stato facile rispondere, in quanto ho prodotto diversi esempi di una recente conferenza, che è possibile scaricare all'indirizzo scriptinganswers.com/essentials/index.php/2008/03/25/techmentor-2008-san-francisco-auditing-examples (in inglese).

Un esempio ha l'obiettivo di elencare diverse proprietà dalla classe Win32_UserAccount in WMI (Windows Management Instrumentation) da diversi computer. Presupponendo che questi nomi di computer siano elencati in C:\Computers.txt, questo semplice comando farà il lavoro:

Gwmi win32_useraccount –comp (gc c:\computers.txt)

Il problema è che la classe Win32_UserAccount non include una proprietà che indica da quale computer proviene ogni istanza, pertanto l'elenco ottenuto sarà un inutile miscuglio di account di molti computer. Risolvo il problema creando un nuovo oggetto personalizzato che include il nome del computer di origine e seleziona le proprietà della classe che mi interessano. Il codice per questo oggetto personalizzato è mostrato nella Figura 2.

Figura 2 Uso di un oggetto personalizzato per raccogliere i nomi dei computer e selezionare le proprietà

function Get-UserInventory {
  PROCESS {
    # $_ is a computer name
    $users = gwmi win32_useraccount -ComputerName $_
    foreach ($user in $users) {
      $obj = New-Object
      $obj | Add-Member NoteProperty Computer $_
      $obj | Add-Member NotePropertyPasswordExpires ($user.PasswordExpires)
      $obj | Add-Member NoteProperty Disabled ($user.Disabled)
      $obj | Add-Member NotePropertyFullName ($user.FullName)
      $obj | Add-Member NoteProperty Lockout ($user.Lockout)
      $obj | Add-Member NoteProperty Name ($user.Name)
      $obj | Add-Member NotePropertyPasswordRequired ($user.PasswordRequired)
      $obj | Add-Member NotePropertyPasswordChangeable ($user.PasswordChangeable)
    }
  }
}

Get-Content c:\computers.txt | 
  Get-UserInventory | 
  where { $_.PasswordRequired -eq $False } | 
  selectComputer,Name | 
  Export-Csv c:\BasUsersBad.csv

La riga di comando finale passa tutti i nomi dei computer alla funzione di filtro, producendo oggetti personalizzati. Successivamente filtro tutti gli utenti tranne quelli la cui proprietà PasswordRequired è False (l'intento è generare un rapporto di controllo degli account problematici). Quindi tengo semplicemente le proprietà Computer e Name in modo che il rapporto finale sia un elenco di nomi di computer e nomi di account che richiedono attenzione in quanto hanno password senza scadenza. La funzione di filtro consente la creazione di questo rapporto di più computer perché aggiunge il nome del computer di origine all'output lasciando solo le proprietà che mi interessa vedere.

Sebbene ci siano altri modi simili per compiere questa attività, questo approccio forse è il più semplice. Serve anche a illustrare concetti e tecniche importanti.

Trovare il tempo per giocare

Anche se si conosce la pipeline, pensare in termini di oggetti fisici può aiutare a immaginarsi cosa si sta cercando di fare.

Pertanto, la prossima volta che ci si trova in difficoltà con un concetto di Windows PowerShell, provare ad allontanarsi dal computer e a replicare l'attività usando oggetti d'uso quotidiano. Consiglio umilmente di tenere sempre a disposizione un sacchetto di palline da ping-pong (o di cubetti se non si riesce a trovarle) proprio per questo scopo.

Cmdlet del mese: Out-File

Quante volte l'output di un cmdlet è stato indirizzato a un file usando il simbolo >? Qualcosa di simile a Get-Process > Processes.txt. Quanti sono a conoscenza del fatto che stanno usando proprio il cmdlet Out-File sotto mentite spoglie? Ecco un comando che esegue esattamente la stessa funzione: Get-Process | Out-File Processes.txt.

Naturalmente richiede un po' più di digitazione, pertanto perché preoccuparsi di digitare per intero il cmdlet Out-File? Un motivo è che Out-File risulta molto più chiaro da leggere. Qualcuno che arrivasse tra sei mesi, ad esempio, potrebbe guardare questo script e chiedersi cosa rappresenti il simbolo > (dopo tutto è una specie di retaggio del passato).

Al contrario, con Out-File sembra piuttosto ovvio che si sta creando un file in cui verrà scritto l'output. Inoltre, Out-File prevede il parametro -append (molto simile a >>), insieme ai parametri -force e -noClobber, e quindi consente di controllare se i file esistenti vengono sovrascritti. Infine, digitando Out-File si ha accesso anche al parametro -whatif. Questo comodo parametro mostra cosa farà Out-File senza farlo nella realtà. Si tratta di un metodo eccezionale per verificare una riga di comando complicata con poco o nessun pericolo.

Don Jones è coautore di Windows PowerShell: TFM e insegna Windows PowerShell (www.ScriptingTraining.com).

© 2008 Microsoft Corporation e CMP Media, LLC. Tutti i diritti riservati. È vietata la riproduzione completa o parziale senza autorizzazione.