Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
PowerShell 2.0
Guida completa
Efran Cobisi
Windows PowerShell 2.0 - Guida completa
Collana:
Nessuna parte del presente libro può essere riprodotta, memorizzata in un sistema che ne
permetta l’elaborazione, né trasmessa in qualsivoglia forma e con qualsivoglia mezzo
elettronico o meccanico, né può essere fotocopiata, riprodotta o registrata altrimenti, senza
previo consenso scritto dell’editore, tranne nel caso di brevi citazioni contenute in articoli di
critica o recensioni.
Parte I – Introduzione
Contiene le informazioni necessarie per il primo avvio di PowerShell,
per eseguire i primi comandi e ottenere indicazioni dalla guida in
linea.
Parte II – Sintassi di base
Raggruppa i capitoli che trattano gli elementi sintattici fondamentali e
la struttura del linguaggio di scripting della shell: una volta terminata
la lettura di questa parte si possono già realizzare i primi script.
Parte III – Elaborazione dei dati
I capitoli di questa parte contengono un’analisi dettagliata delle
principali tipologie di dato supportate da PowerShell e delle
funzionalità messe a disposizione dal framework Microsoft .NET per
gestirle.
Installazione
A partire da Windows 7 e Windows Server 2008 R2, Windows
Management Framework è compreso nel sistema operativo e non è
necessaria alcuna installazione da parte dell’utente; qualora
PowerShell non fosse disponibile tra i programmi installati, tuttavia,
potrebbe essere necessario abilitare manualmente questa
funzionalità, seguendo le istruzioni riportate nella sezione
“Abilitazione di PowerShell su Windows Server 2008” più avanti in
questo capitolo. Le sezioni che seguono, invece, sono dedicate
all’installazione di WMF nelle versioni meno recenti di Windows.
Avviare PowerShell
All’interno del menu Start di Windows, l’installazione (o l’abilitazione)
di PowerShell 2.0 porta alla creazione di una nuova cartella
chiamata Windows PowerShell e di due nuovi collegamenti, sotto la
voce Programmi > Accessori.
L’interfaccia a console
Il primo collegamento, chiamato semplicemente Windows
PowerShell, avvia una classica interfaccia a console che permette di
impartire comandi alla shell e ricevere informazioni utilizzando del
semplice testo.
Figura 1.5 - L’interfaccia a console testuale di PowerShell 2.0.
Interfacce alternative
Sia l’interfaccia a console testuale sia PowerShell ISE sono solo un
mezzo per dialogare con la shell, il cui funzionamento è determinato
dal codice presente in alcune librerie di sistema installate con
Windows Management Framework. Entrambe le interfacce, infatti, in
gergo sono definite host di PowerShell, ovvero applicativi che
ospitano il “motore” di questa piattaforma.
Grazie a questa separazione tra logica di funzionamento e
interfaccia, Microsoft ha reso possibile lo sviluppo di interfacce
alternative per PowerShell; al momento in cui questo libro viene
scritto, su Internet sono disponibili ben quattro interfacce alternative
alle due fornite da Microsoft, la maggior parte delle quali è gratuita. Il
seguito di questo paragrafo le illustra brevemente.
PowerGUI è un’interfaccia grafica gratuita sviluppata da Quest
Software (http://www.quest.com), che ricalca l’aspetto di PowerShell
ISE e include un ottimo editor di comandi, dotato anch’esso di un
sistema di colorazione del codice in base alla sintassi. Tra le
caratteristiche di PowerGUI spiccano la possibilità di utilizzare con
facilità le librerie di comandi create dagli utenti della community del
prodotto e l’eccellente supporto per il test del codice, che potrebbe
ricordare quello offerto dai classici strumenti di sviluppo software.
Grazie al contributo di Stefano Del Furia, powershell.it collabora con
Quest Software affinché PowerGUI sia disponibile anche in lingua
italiana.
Figura 1.7 - Una schermata di PowerGUI.
Il prompt
La riga del prompt contiene, di default, alcune informazioni relative
alla sessione di lavoro corrente: una tipica riga comincia con il testo
PS – acronimo di PowerShell – seguito dalla directory di lavoro
corrente, un’indicazione molto utile quando, per esempio, si stanno
effettuando delle manutenzioni su file system e sono coinvolte più
cartelle differenti; all’avvio della shell la directory di lavoro
corrisponde alla cartella dell’utente che ha avviato l’applicazione.
Quando se ne presenta la necessità, il prompt fornisce
automaticamente informazioni aggiuntive all’operatore. Se si sta
lavorando all’interno di una sessione remota (tema trattato nel
Capitolo 27), PowerShell aggiunge in testa alla riga di prompt il
nome della macchina su cui si sta operando, per consentire
all’utente di verificare in modo rapido dove sta eseguendo le proprie
istruzioni ed evitare così potenziali problemi; in fase di debug, inoltre,
la shell aggiunge al testo del prompt la sigla [DBG]:.
I cmdlet
L’architettura di PowerShell garantisce che tutti i cmdlet siano
usufruibili secondo regole precise e ben documentate, a cominciare
dal nome: ogni cmdlet, infatti, ha un nome del tipo Verbo-
Sostantivo (come Remove-Item, Export-Csv, Disable-ComputerRestore ecc.),
così che sia semplice determinare lo scopo di un blocco di istruzioni
e più immediato ricordare quale comando utilizzare in una
determinata situazione.
La shell non fa distinzione tra minuscole e maiuscole, quando si
tratta di nomi di cmdlet; Get-Date è equivalente a get-date e a gEt-DaTe, ma
per questioni di stile e leggibilità è consigliabile utilizzare sempre la
prima forma.
I parametri
Ogni cmdlet espone diversi parametri, tramite i quali è possibile
variarne il funzionamento e la logica delle istruzioni. Insieme
all’organizzazione e alla nomenclatura dei cmdlet, PowerShell rende
finalmente universale e chiaramente documentato anche il sistema
di valorizzazione (in gergo parsing) e di passaggio dei parametri
verso ciascun comando, poiché queste attività non sono delegate ai
singoli tool (come avviene per altre shell) ma al motore stesso della
piattaforma.
Per specificare il valore di un parametro è sufficiente accodare al
nome del cmdlet, separato da uno spazio, un trattino (-) e il nome del
parametro di interesse, seguito nuovamente da uno spazio e dal
valore che questo deve assumere. La logica si ripete nel caso di
impostazione di più parametri, dove per separare l’indicazione di un
parametro dall’altro si usa nuovamente uno spazio.
Il cmdlet Get-process, per esempio, recupera la lista dei processi in
esecuzione in un computer (di default il computer locale) e dispone
di un parametro, -Name, tramite il quale filtrare i processi ritornati per
nome.
Eseguendo le istruzioni che seguono, PowerShell ritorna la lista dei
processi attivi nella macchina locale il cui nome è “iexplore” (ovvero
le istanze di Internet Explorer):
Conferma
Eseguire l’operazione?
Esecuzione dell’operazione "Stop-Process" sulla destinazione "calc (1644)".
[S] Sì [T] Sì a tutti [N] No [U] No a tutti [O] Sospendi [?] Guida (il
valore predefinito è "S"):
Anche nei nomi dei parametri (e, di riflesso, nelle loro abbreviazioni)
PowerShell non fa distinzione tra minuscole e maiuscole.
PS C:\Users\ikmju> Get-Command
NOME
Get-Command
RIEPILOGO
Ottiene informazioni di base sui cmdlet e altri elementi dei comandi di Windows
PowerShell.
SINTASSI
Get-Command [[-Name] <string[]>] [-CommandType {Alias | Function |
Filter | Cmdlet | ExternalScript | Application |
Script | All}] [[-ArgumentList] <Object[]>] [-Module <string[]>] [-
Syntax] [-TotalCount <int>] [<CommonParameters>
]
[...]
DESCRIZIONE
Il cmdlet Get-Command ottiene informazioni di base relative a cmdlet e
ad altri elementi dei comandi di Windows PowerShell nella sessione, quali
alias, funzioni, filtri, script e applicazioni.
[...]
COLLEGAMENTI CORRELATI
Online version: http://go.microsoft.com/fwlink/?LinkID=113309
about_Command_Precedence
Get-Help
Get-PSDrive
[...]
COMMENTI
Per visualizzare gli esempi, digitare: "get-help Get-Command -examples".
[...]
-Examples di Get-Help:
---------------------------ESEMPIO 1 -------------------------
C:\PS>get-command
Descrizione
-----------
Con questo comando si ottengono informazioni su tutti i cmdlet e le
funzioni di Windows PowerShell.
PARAMETRI
[...]
-CommandType <CommandTypes>
Ottiene solo i tipi di comandi specificati. Utilizzare "CommandType"
o il relativo alias, "Type". Per impostazione predefinita, Get-Command
consente di ottenere cmdlet e funzioni.
PARAMETRI
[...]
-CommandType <CommandTypes>
[...]
Obbligatorio? false
Posizione? named
Valore predefinito
Accettare input da pipeline? true (ByPropertyName)
Accettare caratteri jolly? False
[...]
INPUT
System.String
È possibile reindirizzare una proprietà "Name", "Command" e "Verb"
specificata o un oggetto stringa a Get-Comma
nd.
[...]
OUTPUT
Object
Il tipo di oggetto restituito dipende dal tipo di elemento del comando
recuperato. Get-Command in un cmdlet, per esempio, recupera un oggetto
CmdletInfo, mentre Get-Command in una DLL recupera un oggetto ApplicationInfo.
[...]
PS C:\Users\ikmju> C:\test.txt
PowerShell, infine, supporta gli script creati per gli interpreti dei
comandi CMD e la piattaforma Windows Script Host (WSH), che
comprende cscript e wscript.
In modo automatico, infatti, è possibile lanciare dalla shell sia i file
batch gestiti dal primo sistema (con estensione .bat o .cmd) sia gli
script eseguibili dal secondo (con estensione .vbs o .js).
Nonostante l’output generato da questi strumenti sia integrato con
quello della shell, dietro le quinte la piattaforma esegue in maniera
trasparente i diversi interpreti e ne dirotta l’output (e l’input) verso la
finestra principale.
Nel caso di CMD è possibile, per esempio, creare un file batch con
nome Hello.bat, inserirvi il contenuto che segue e memorizzarlo
all’interno della cartella C:\ del sistema:
PS C:\Users\ikmju> C:\hello.bat
Un saluto da CMD...
WScript //H:CScript
Un saluto da WSH...
WScript //H:WScript
Le modalità di parsing
Per consentire la convivenza della piattaforma di scripting e
dell’ambiente di esecuzione dei comandi, quando interpreta una riga
la shell può impiegare due diverse modalità di parsing, a seconda
della necessità.
La modalità di parsing delle espressioni permette di recuperare il
risultato di un’operazione che coinvolge uno o più valori e che vede
coinvolto il motore di scripting della shell, come per esempio una
somma di numeri.
La modalità di parsing dei comandi, invece, consente di eseguire
un cmdlet o un eseguibile esterno.
L’attivazione dell’una o dell’altra modalità avviene in base ai primi
caratteri individuati nella riga, tralasciando gli eventuali spazi iniziali:
se si tratta di una keyword o di un simbolo previsto dal linguaggio di
scripting (elementi approfonditi nel seguito del libro) oppure di una
cifra allora il parsing avviene per le espressioni. In tutti gli altri casi la
shell effettua il parsing per un comando.
È possibile combinare le due diverse modalità di parsing all’interno
di una stessa riga, racchiudendo la porzione di testo di interesse
all’interno di una coppia di parentesi tonde (). Così facendo, si
obbliga la shell a rivalutare la modalità di parsing da adottare per il
codice desiderato.
In alcuni casi la shell permette una convivenza pacifica tra le due modalità di
parsing all’interno della stessa riga anche omettendo le parentesi tonde di
NO
raggruppamento descritte poc’anzi: tuttavia, per una maggiore chiarezza
TA
espositiva, in questo libro si è scelto di utilizzare in ogni occasione questa struttura
sintattica, anche laddove non fosse strettamente necessaria.
Gli oggetti
L’approccio impiegato da PowerShell per risolvere il problema della
ricostruzione dei dati scambiati da un comando all’altro è davvero
innovativo e si basa sull’assunto che ogni informazione gestita da
questa shell è sempre rappresentata da un oggetto del framework
Microsoft .NET, dove il termine oggetto è utilizzato per indicare un
pacchetto strutturato di informazioni e funzionalità correlate a
queste. Non solo, dunque, è possibile usare degli oggetti all’interno
dell’ambiente di scripting di PowerShell, ma gli stessi cmdlet
ritornano e accettano come argomenti degli oggetti! Le funzionalità e
la tipologia di informazioni supportate da ciascun oggetto sono
definite dal suo tipo (chiamato anche classe, per analogia con i
linguaggi di derivazione C), che consiste in uno schema definito
all’interno di una libreria o di un modulo eseguibile. Il sistema dei tipi
di Microsoft .NET è aperto e chiunque ne può creare e aggiungere:
gli elementi utilizzati con maggiore frequenza, tuttavia, sono quelli
compresi con il framework Microsoft .NET che, per tale ragione,
sono chiamati nativi.
Quando un oggetto è definito da una particolare classe si dice che è
un’istanza di quest’ultima, dove oggetto e istanza sono sinonimi ma
il secondo termine rivela con più precisione la natura del dato.
Nonostante la shell visualizzi a video un testo, il comando Get-Date,
per esempio, ritorna un oggetto strutturato che incorpora diverse
informazioni sulla data e l’ora correnti: diversamente da quanto
potrebbe sembrare, infatti, l’informazione comprende anche il
numero di millisecondi e l’eventuale indicazione sull’ora legale,
anche se queste informazioni non sono direttamente visualizzate.
NO Il meccanismo in base a cui la shell determina quale dato visualizzare a video per
TA ciascun tipo di oggetto è analizzato nel Capitolo 9.
Le proprietà d’istanza
Quasi tutte le informazioni contenute all’interno di un oggetto sono
tipicamente disponibili attraverso le proprietà esposte da
quest’ultimo. Ogni proprietà consiste in un ulteriore oggetto
secondario, a cui è stato attribuito un nome: per recuperare o
elaborare il valore associato a una proprietà di un oggetto è
sufficiente utilizzare il simbolo punto (.) tra l’oggetto di riferimento e il
nome della proprietà desiderata, secondo questo semplice schema:
<Oggetto>.<Proprietà>
PS C:\Users\ikmju> (Get-Date).DayOfYear
90
I metodi d’istanza
I metodi d’istanza sono blocchi di funzionalità che interagiscono con
l’oggetto di cui fanno parte, talvolta modificandolo oppure
semplicemente ritornando un risultato. A ogni metodo è assegnato
un nome e, in modo simile alle proprietà, per invocare un metodo di
un oggetto è sufficiente utilizzare il simbolo punto (.) tra l’oggetto di
riferimento e il nome del metodo desiderato, cui è necessario far
seguire una coppia di parentesi tonde().
Ciascun metodo può accettare un numero di parametri prestabilito
dalla definizione del metodo stesso (fornita nella classe dell’oggetto
di appartenenza), tramite i quali è generalmente possibile variare il
funzionamento del codice: quando un metodo accetta dei parametri
è necessario specificare il valore di ognuno all’interno della coppia di
parentesi tonde di chiamata, rispettando l’ordine della definizione e
separando ogni parametro con il simbolo virgola (,).
La sintassi di chiamata dei metodi può essere schematizzata così:
PS C:\Users\ikmju> (Get-Date).IsDaylightSavingTime()
True
PS C:\Users\ikmju> (Get-Date).AddDays(907).AddDays(50).IsDaylightSavingTime()
False
Il metodo ToString()
Ogni oggetto del framework Microsoft.NET è tenuto a esporre alcuni
metodi di base per via di un meccanismo chiamato ereditarietà, che
permette di derivare la definizione di un tipo dall’altro e formare una
gerarchia di classi. Senza scendere nel dettaglio di questo
argomento, destinato a un pubblico di soli sviluppatori, vale la pena
di osservare che, tra i metodi menzionati, ve n’è uno chiamato
ToString() che permette di ottenere sempre una rappresentazione
testuale dell’istanza a cui è associato.
Nello script che segue, per esempio, si utilizza il metodo Tostring()
per forzare la shell a produrre una rappresentazione testuale di
alcuni oggetti:
PS C:\Users\ikmju> (Get-Date).ToString()
31/03/2010 16.57.17
PS C:\Users\ikmju> (Get-Date).DayOfYear.ToString()
90
PS C:\Users\ikmju> (Get-Date).DayOfYear.ToString().ToString()
90
Nel seguito del libro sono analizzati i principali membri per le classi più utilizzate
NO
del framework Microsoft .NET. La documentazione ufficiale per ciascun tipo e
TA
ciascun membro è disponibile sul sito Microsoft MSDN.
Le variabili
All’interno di PowerShell, una variabile consiste in un riferimento
temporaneo a un’istanza di un oggetto, dotato di nome. È possibile
utilizzare variabili all’interno di una sessione della shell per
contenere valori quali numeri, testi o qualsiasi altro oggetto del
framework Microsoft .NET.
Una variabile è identificata dal proprio nome, che in base alla
sintassi deve iniziare con il simbolo del dollaro ($) e può contenere
un numero qualsiasi di lettere e cifre, in modo simile a questi
esempi:
• $x
• $test
• $fileCountl23
• $9783
• ${xy#z}
L’assegnazione
L’impostazione del valore di una variabile avviene mediante
l’operatore di assegnazione, individuato dal simbolo di uguale (=)
frapposto tra la variabile e l’oggetto di partenza. La sintassi per
questo tipo di operazione è schematizzata come segue:
<Variabile> = <Oggetto>
PS C:\Users\ikmju> $test
TypeName: System.DateTime
PS C:\Users\ikmju> $test.DayOfYear
90
PS C:\Users\ikmju> $test.IsDaylightSavingTime()
True
TypeName: System.IO.FileInfo
Le variabili automatiche
Le variabili automatiche sono un particolare insieme di variabili il cui
valore è impostato da PowerShell durante la sessione di lavoro. La
variabile $PID, per esempio, contiene un numero che corrisponde
all’identificativo del processo che ospita la sessione di PowerShell,
mentre $LastExitCode contiene il codice di uscita dell’ultimo eseguibile
nativo Windows lanciato dalla shell.
Le variabili automatiche sono fondamentali per il funzionamento
della shell e le più importanti sono illustrate assieme ai concetti cui
sono legate, nel seguito del libro; la Tabella 3.1, inoltre, elenca
alcune voci di utilità generale.
I tipi di oggetto
Come anticipato nella prima parte di questo capitolo, ogni tipo (o
classe) di oggetto descrive le funzionalità e la tipologia di
informazioni supportate dalle istanze che gli fanno capo. Ogni tipo è
contenuto in una libreria (o in un eseguibile), gestita dal framework
Microsoft .NET che, quando è caricata all’interno della sessione di
PowerShell, consente di utilizzare i tipi che espone e di individuarli in
base al loro nome; ogni libreria (o eseguibile) di questo tipo prende il
nome di assembly. All’avvio di ogni sessione, PowerShell carica di
default i principali assembly del sistema, garantendo all’utente la
disponibilità immediata delle classi utilizzate più di frequente.
Così come avviene in molte altre piattaforme di sviluppo, per cercare
di renderne più ordinato e gestibile l’insieme, all’interno del
framework Microsoft .NET le classi disponibili sono dichiarate
all’interno di contenitori dotati di un nome identificativo, chiamati
namespace (o spazi dei nomi, in italiano). I namespace possono
essere nidificati l’uno nell’altro, per raggruppare tra loro delle classi
che condividono lo stesso obiettivo o appartengono allo stesso
ambito; il nome completo di un tipo è dato dalla combinazione della
gerarchia dei namespace cui appartiene e del nome del tipo stesso,
dove ciascun elemento è separato dal successivo per mezzo del
simbolo punto (.).
Per visualizzare il nome completo del tipo di un oggetto è possibile
utilizzare il cmdlet Get-Member, prestando attenzione alla prima riga
visualizzata a video:
TypeName: System.IO.FileInfo
[...]
TypeName: System.DateTime
[...]
I tipi primitivi
Nonostante il framework Microsoft .NET comprenda nativamente
alcune decine di migliaia di classi, alcune tra queste costituiscono le
basi su cui ogni altra classe è costruita. Questi elementi sono
chiamati tipi primitivi e svolgono un ruolo essenziale nella creazione
di qualsiasi script all’interno di PowerShell. Il seguito del libro dedica
ampio spazio a ciascuno mentre questa sezione ne riepiloga
brevemente gli obiettivi e le potenzialità, in base all’ambito.
Ambito logico:
• System.Boolean esprime un valore logico, vero o falso, ed è alla base del
controllo di qualsiasi flusso di esecuzione del codice.
Ambito numerico:
• System.Byte corrisponde a un valore numerico intero ad 8 bit ed è spesso
utilizzato per elaborare il contenuto di file binari;
• rappresentano numeri interi a 16 bit e sono
System.Int16 e System.uinti6
talvolta impiegati per memorizzare piccoli valori numerici interi;
• System.Int32 e System.UInt32rappresentano numeri interi a 32 bit e sono
generalmente utilizzati per memorizzare qualsiasi valore numerico intero;
• rappresentano numeri interi a 64 bit e sono
System.Int64 e System.UInt64
impiegati solo quando la capienza dei tipi precedenti non è sufficiente a
immagazzinare un valore intero molto grande;
• Valore numerico decimale a 128 bit, impiegato per
System.Decimai.
memorizzare valori decimali con la massima precisione possibile;
• System.single.Numero in virgola mobile a 32 bit, usato per memorizzare
valori decimali molto grandi o molto piccoli, con una precisione limitata;
• System.Double. Numero in virgola mobile a 64 bit, usato con gli stessi
obiettivi del precedente ma garantendo una precisione doppia rispetto a
esso.
Ambito testuale:
• System.char rappresenta un singolo carattere Unicode ed è talvolta
utilizzato per ottenere un carattere in base al rispettivo codice numerico;
System.string rappresenta un testo organizzato in base a una sequenza
ordinata di caratteri, chiamata stringa: i metodi e le proprietà di questa
classe permettono di manipolare qualsiasi testo.
PS C:\Users\ikmju> $x = 123
PS C:\Users\ikmju> $x.GetType().FullName
System.Int32
PS C:\Users\ikmju> (123456).GetType().FullName
System.Int32
PS C:\Users\ikmju> (1234567890).GetType().FullName
System.Int32
PS C:\Users\ikmju> (12345678901234).GetType().FullName
System.Int64
PS C:\Users\ikmju> (1234567890123456789).GetType().FullName
System.Int64
PS C:\Users\ikmju> (1.23).GetType().FullName
System.Double
PS C:\Users\ikmju> (2348123213213321.23498723422).GetType().FullName
System.Double
PS C:\Users\ikmju> (1.23d).GetType().FullName
System.Decimal
New-Object
La shell mette a disposizione il cmdlet New-Object per creare un oggetto
partendo dal nome del tipo desiderato, da fornire al parametro
posizionale -TypeName secondo questa sintassi:
New-Object <NomeTipo>
TypeName: System.Version
PS C:\Users\ikmju> [System.IO.FileInfo].FullName
System.IO.FileInfo
PS C:\Users\ikmju> [IO.FileInfo].FullName
System.IO.FileInfo
NO Il sito Microsoft MSDN contiene le informazioni sui costruttori di tutte le classi del
TA framework Microsoft .NET e sui valori che questi si attendono.
TypeName: System.Int32
Name MemberType Definition
---- ---------- ----------
[...]
MaxValue Property static System.Int32 MaxValue {get;}
MinValue Property static System.Int32 MinValue {get;}
TypeName: System.DateTime
[<NomeTipo>]::<Proprietà>]
[<NomeTipo>]::<Metodo>(...)
PS C:\Users\ikmju> [Int32]::MaxValue
2147483647
PS C:\Users\ikmju> [DateTime]::DaysInMonth(2100, 2)
28
Riferimenti null
Quando una variabile non fa riferimento ad alcun oggetto si dice che
è nulla e il suo valore è pari a quello della variabile automatica $null.
Alcuni metodi degli oggetti esposti dal framework Microsoft .NET
ritornano un valore nullo nei casi in cui non vi sia alcuna
informazione da ritornare al chiamante.
Le variabili pari a $null non fanno capo ad alcun tipo e il tentativo di
richiamarne un qualsiasi metodo d’istanza (compresi quelli di base,
come astringo) genera un errore, come evidenziato dal codice che
segue:
Il cast
Il cast (o casting) è un’operazione che consente di convertire un
oggetto in un altro, in base a un tipo specifico. PowerShell amplifica
il concetto di cast presente in altri linguaggi di sviluppo e attiva, per
ogni operazione di questo tipo, un potente motore di conversione, in
grado di raggiungere il più delle volte il risultato sperato. Per
effettuare il cast di un oggetto in un altro tipo è sufficiente far
precedere al primo il nome del tipo del secondo, avendo cura di
racchiudere quest’ultimo tra parentesi quadre ([]). Lo schema della
sintassi è il seguente:
[<NomeTipo>] <OggettoOriginale>
I type accelerator
Tutta la semantica del linguaggio di scripting di PowerShell è nata
per consentire all’utente di impegnare la minore quantità di tempo
possibile per raggiungere i propri obiettivi. I type accelerator sono tra
le caratteristiche che facilitano questo proposito e consistono nella
possibilità di utilizzare stringhe molto brevi per far riferimento a nomi
di tipo molto più lunghi. Per impiegare il tipo
System.Text.RegularExpressions.Regex, per esempio, che nel framework
Microsoft .NET rappresenta un’espressione regolare, è possibile
utilizzare il type accelerator regex, rendendo il codice più leggibile e
sintetico:
Gli operatori
Gli operatori binari sono particolari elementi sintattici del linguaggio
di scripting della shell che consentono di effettuare un’operazione tra
due oggetti per ritornarne, come risultato, un terzo. A questi si
contrappongono gli operatori unari, così chiamati perché, a
differenza dei primi, operano a fronte di un unico oggetto.
Ogni operatore è individuato da un simbolo: nel caso degli operatori
binari questo va interposto tra gli oggetti su cui è chiamato ad agire,
che assumono il nome di operando di sinistra e operando di
destra. Per gli operatori unari, invece, il simbolo va fatto seguire
(con alcune eccezioni) all’unico operando.
La sintassi degli operatori binari è schematizzabile come segue:
<Operando><Operatore>
PS C:\Users\ikmju> $test = 3 + 2
PS C:\Users\ikmju> $test
5
PS C:\Users\ikmju> 97.8 % 3
1.8
PS C:\Users\ikmju> $x = 9
PS C:\Users\ikmju> $x++
PS C:\Users\ikmju> $x
10
PS C:\Users\ikmju> $x = "test"
PS C:\Users\ikmju> $x++
L'operatore '++' può essere utilizzato solo su numeri- L'operando è un
oggetto 'System.String'
In riga:1 car:5
+ $x++ <<<<
+ Categorylnfo : Invalidoperation: (test:String) [], RuntimeException
Lavorare con gli script
I commenti
Poiché gli script esterni contengono tipicamente svariate righe di
istruzioni differenti, risulta utile inserire dei commenti testuali
all’interno dei file di codice. Questa pratica, largamente utilizzata
dagli sviluppatori di software di tutto il mondo, consente di
documentare i diversi passaggi delle operazioni effettuate all’interno
degli script e rende molto più semplice la manutenzione del codice.
Per inserire una riga di commento all’interno del codice è sufficiente
anteporre al testo desiderato il carattere cancelletto (#): la shell
ignora questo simbolo e tutto ciò che lo segue.
Tutte le considerazioni fatte per gli script fino a qui valgono anche
per il prompt, dove, di conseguenza, digitando una riga di solo
commento non viene prodotto alcun output:
I criteri di esecuzione
A differenza dei cmdlet, i cui obiettivi sono ben definiti, isolati e
documentati, gli script esterni potrebbero potenzialmente contenere
istruzioni dannose per il sistema, a insaputa dell’utilizzatore: per tale
ragione, nel pieno rispetto della Microsoft Trustworthy Initiative - il
documento di visione e strategia sulla sicurezza informatica
approvato nel 2002 dall’allora CTO di Microsoft, Craig Mundie -
PowerShell implementa un meccanismo automatico di verifica e
blocco degli script esterni, chiamato criterio di esecuzione
(execution policy). Questa funzionalità non pone vincoli sui comandi
effettivamente eseguibili, però consente comunque di stabilire alcune
semplici regole di esecuzione e di evitare che gli utenti le violino
inconsapevolmente. Il criterio di esecuzione può assumere sei
possibili differenti livelli, dove Restricted è il valore predefinito e non
consente l’esecuzione di alcuno script esterno. I due livelli
immediatamente successivi impediscono alla shell di eseguire script
a meno che non dispongano di firma digitale: mentre AllSigned
verifica questa norma per qualsiasi script esterno, RemoteSigned
limita l’analisi ai soli file scaricati da Internet. Il livello Unrestricted
non richiede alcuna firma digitale ma comporta la visualizzazione di
un avviso non appena si cerchi di eseguire script scaricati da
Internet. Il livello Bypass, infine, non impone alcuna restrizione né
comporta la visualizzazione di alcun messaggio di avviso. Benché
compaia nella lista dei possibili valori dei criteri di esecuzione, il
valore Undefined, invece, indica l’assenza di un’impostazione.
Esistono cinque differenti ambiti (scope) in cui è possibile applicare i
criteri appena esposti e la shell determina quale livello utilizzare in
base all’ordine di precedenza degli ambiti:
• i prime due ambiti, Configurazione computer e Configurazione
utente, sono impostabili tramite i criteri di gruppo di Windows;
• l’ambito successivo, Process, governa l’esecuzione del
processo corrente: lanciando l’eseguibile di PowerShell è
possibile indicare il criterio di esecuzione per l’ambito Process
mediante il parametro -ExecutionPolicy;
• l’ambito CurrentUser, poi, afferisce all’utente che ha eseguito il
processo della shell;
• LocaiMacnine regola l’esecuzione all’interno della macchina
corrente.
Per recuperare il criterio di esecuzione attivo all’interno della shell è
possibile eseguire il cmdlet Get-ExecutionPolicy:
PS C:\Users\ikmju> Get-ExecutionPolicy
Restricted
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Undefined
LocalMachine RemoteSigned
Hello, world
Per il lettore con precedenti esperienze legate al mondo dello
sviluppo software, il titolo di questa sezione rappresenta sicuramente
l’emblema del codice sorgente: per anni si è tentato di dimostrare la
semplicità d’uso dei linguaggi di programmazione utilizzando un
piccolo saggio delle loro funzionalità, mediante un blocco di istruzioni
che visualizza a video proprio la scritta “hello, world”.
Data la natura e gli obiettivi del sistema, PowerShell permette di
visualizzare facilmente del testo nell’interfaccia dell’host e dispone di
alcuni metodi differenti per produrre tale risultato: il resto del capitolo
ne adopera uno tra quelli messi a disposizione dalla shell, nel
tentativo di ricreare la tradizionale atmosfera di “hello, world”,
respirata dagli sviluppatori di tutto il mondo, anche in PowerShell.
Il codice che segue utilizza il cmdlet Write-Host per visualizzare la
famosa scritta a video; si tratta di un cmdlet tra i più semplici, che
genera una rappresentazione testuale del valore fornito tramite il
parametro –Object (unico posizionale). Nel caso delle stringhe,
ovviamente, viene utilizzato direttamente il valore fornito.
PS C:\Users\ikmju> HelloWorld.ps1
hello, world
PS C:\Users\ikmju> HelloWorld
hello, world
Nel caso lo script sia memorizzato in una directory non compresa nel
path, invece, la shell richiede che il nome del file sia accompagnato
dal suo percorso.
PS C:\Users\ikmju> C:\Users\ikmju\HelloWorld.ps1
hello, world
La regola vale anche per gli script che risiedono nella directory
corrente: PowerShell in questo caso richiede che sia specificato il
percorso relativo .\:
PS C:\Users\ikmju> .\HelloWorld.ps1
hello, world
Lo scope
PowerShell, di default, mantiene isolate le variabili definite negli
script esterni, in maniera tale che il chiamante non possa accedervi
né modificarne i valori. Questa pratica, chiamata in gergo scope, è
molto utilizzata nei linguaggi di sviluppo moderni perché consente di
incapsulare funzionalità indipendenti le une dalle altre e di facilitare,
quindi, il riutilizzo del codice.
La logica che governa gli scope è semplice: se una variabile (o un
altro tra gli elementi esposti nel seguito del libro) è definita a livello
globale (global scope) – ovvero al di fuori di qualsiasi script – allora è
visibile ovunque, script compresi, ma modificabile solo da
un’istruzione eseguita a livello globale.
Se invece la variabile è definita a livello di script (script scope) allora
è visibile e modificabile solo da un’istruzione eseguita a livello dello
stesso script e risulta invisibile nel resto del codice.
Quando, poi, all’interno di un primo script si esegue un secondo
script, le variabili definite nel primo script sono visibili a entrambi (non
a livello globale) ma risultano modificabili solo dal codice del primo,
mentre le variabili definite nel secondo sono visibili e modificabili solo
da quest’ultimo ma invisibili altrove.
Lo stesso ragionamento si applica nel caso in cui è il secondo script
a richiamarne un terzo e così via: in sostanza, le variabili definite
all’interno di un particolare scope sono visibili all’interno dello scope
stesso e degli scope aperti da quest’ultimo eseguendo altri script, ma
sono modificabili solo all’interno del primo.
Nell’esempio che segue sono definiti due script indipendenti,
chiamati Primo.PSI e Secondo.PSI, creati e richiamati con lo scopo di
rendere più chiari i concetti appena esposti. Il contenuto di primo.PSI è
il seguente:
# Contenuto di Primo-PSI
$a = 'Primo'
$b = 'Primo'
'Esecuzione di Secondo.PSI...'
.\Secondo.PSI
'Fine esecuzione di Secondo.PSI...'
# Contenuto di Secondo.PSI
$b = 'Secondo'
$c = 'Secondo'
'$a -> ' + $a
'$b -> ' + $b
'$c -> ' + $c
$a = 'Globale'
'Esecuzione di Primo.PSI...'
.\Primo.PSI
'Fine esecuzione di Primo.PSI...'
$a -> Globale
$b ->
$c ->
Esecuzione di Primo.PSI...
$a -> Primo
$b -> Primo
$c ->
Esecuzione di Secondo.PSI...
$a -> Primo
$b -> Secondo
$c -> Secondo
Fine esecuzione di Secondo.PSI...
$a -> Primo
$b -> Primo
$c ->
Fine esecuzione di Primo.PSI...
$a -> Globale
$b ->
$c ->
Come si può notare, dunque, all’uscita di ogni scope le variabili sono
ripristinate al valore assunto prima dell’entrata, di fatto isolandole.
PowerShell consente, però, di aggirare le regole di isolamento e
protezione dello scope. Può essere utile, per esempio, definire una
variabile in uno script esterno e renderla disponibile,
successivamente all’esecuzione, nello scope del chiamante, così
come può essere necessario nascondere una variabile definita in
uno scope agli scope figlio, per meglio proteggere, per esempio, la
logica di un particolare script. La shell risponde a queste esigenze
permettendo di indicare lo scope al quale ci si riferisce quando si
recupera o si imposta il valore di una determinata variabile: è
sufficiente inserire il nome dello scope, seguito dal simbolo dei due
punti (:), tra il consueto carattere dollaro ($) e il nome della variabile.
Tutti i cmdlet illustrati nel capitolo precedente e che operano sulle
variabili, inoltre, consentono di indicare lo scope desiderato
specificandone il nome tramite il parametro -Scope.
I nomi ammessi per indicare gli scope sono Global, Script, Local e
Private.
$global:test = 'Global'
$script:test = 'Script'
$test = 'Nessuna indicazione'
$global:test = 'Global'
$script:test = 'Script'
$test
$test = 'Nessuna indicazione'
$test
Il local scope
Il local scope (Local) rappresenta in ogni momento lo scope corrente
e corrisponde sempre, quindi, a uno dei due valori precedenti (o la
shell sta eseguendo uno script esterno oppure no). Il local scope è lo
scope predefinito quando si recupera o si imposta il valore di una
variabile.
Le tre righe che seguono, quindi, producono il medesimo risultato se
eseguite all’interno del global scope:
$global:test = 'Test'
$local:test = 'Test'
$test = 'Test'
Il private scope
Il private scope (Private), infine, corrisponde allo scope corrente ma,
a differenza di tale valore, le variabili definite con questa opzione
sono visibili e modificabili solo nello scope di definizione ma non
negli scope figlio, come invece avviene normalmente: la normale
propagazione della visibilità di una variabile negli scope figlio, quindi,
è interrotta.
Tramite questo scope, per esempio, è possibile isolare le variabili
definite all’interno di uno script in maniera tale che siano manipolabili
e visibili unicamente tramite il codice dello script stesso.
Creando, quindi, uno script PrivateScopeTest.PSI con questo contenuto:
$private:test = 'Primo'
.\PrivateScopeTest2.ps1
$test
.\PrivateScopeTest2.ps1
Il dot sourcing
PowerShell consente, inoltre, di lanciare qualsiasi script esterno
evitando l’apertura di un nuovo scope figlio, eseguendone il codice
all’interno dello scope corrente. Questa funzionalità è nota come dot
sourcing – termine che richiama la sintassi impiegata – e consente,
quindi, di evitare completamente il meccanismo d’isolamento e
protezione delle variabili. Per lanciare uno script in questa modalità è
sufficiente impiegare la stessa sintassi dell’operatore di chiamata &,
utilizzando il simbolo del punto (.) al posto del simbolo &.
È possibile, per esempio, definire uno script chiamato
DotSourcingTest.PSI come segue:
$private:x = 'uno'
$script:y = 'due'
$z = 'tre'
ed eseguire questo blocco di istruzioni alla shell (si noti il punto che
precede l’indicazione dello script):
. .\DotSourcingTest.PSI
$x
$y
$z
I parametri
Isolare il codice utilizzato più di frequente negli script esterni porta
senz’altro a un’organizzazione eccellente del proprio lavoro e a un
notevole risparmio di tempo. Talvolta, però, sorge la necessità di
variare solo leggermente le operazioni effettuate da uno script,
cambiando, per esempio, il valore fornito a un cmdlet.
Per venire incontro a questa necessità, PowerShell consente, alla
stregua di quanto è possibile fare con i cmdlet, di specificare dei
parametri anche per gli script.
L’istruzione param
Per definire i parametri di uno script esterno è sufficiente utilizzare
l’istruzione param e indicare, all’interno di una coppia di parentesi
tonde, la lista dei nomi dei parametri accettati, separati dal carattere
virgola:
param ($processID)
PS C:\Users\ikmju> .\ParamTest.PSI o
[...]
param ([Int32]$processId)
PS C:\Users\ikmju> .\MyInvocationTest.PSI
param ($commandName)
Get-Help $commandName -Online
PS C:\Users\ikmju> dir
PS C:\Users\ikmju> Get-Help ls
NOME
Get-ChildItem
RIEPILOGO
Ottiene gli elementi e gli elementi figlio in una o più posizioni
specificate.
[...]
Come anticipato, tra gli elementi elencati da Get-Alias molti hanno una
diretta corrispondenza con il relativo comando delle shell classiche
più conosciute, come CMD e COMMAND.COM sotto Windows e Bash, Ksh nei
sistemi *nix: le Tabelle 6.1 e 6.2 ne elencano alcuni tra gli alias più
importanti per i due sistemi citati.
Nel caso esista già un alias con il nome indicato tramite -Name, il
cmdlet genera un errore:
Alias persistenti
Come anticipato, al termine della sessione gli alias definiti dall’utente
sono completamente rimossi dal sistema; anche se gli alias
predefiniti sembrano godere di un trattamento differente, che pare
mantenerli in vita tra una sessione e l’altra, in realtà questi ultimi
sono caricati automaticamente all’avvio di PowerShell, alla stregua
di un inserimento manuale da parte di un operatore.
Per favorire un facile riutilizzo anche dei cmdlet definiti dall’utente, la
shell mette a disposizione due cmdlet in grado di esportare e
importare gli alias utilizzabili nella sessione corrente.
Il cmdlet Export-Alias consente di esportare tutti o una parte degli alias
attivi all’interno di un file. Si tratta di default di un file in formato CSV
(Comma-Separated Value), ovvero un testo i cui valori sono separati
da virgola. Tuttavia, specificando tramite il parametro -As il valore
Script, è possibile generare uno script PowerShell che contiene una
riga di chiamata a Set-Alias per ogni elemento esportato.
Eseguendo questa riga di codice, tutti gli alias della sessione
corrente sono esportati in formato CSV nel file Alias.csv:
La logica booleana
Così come avviene per la maggior parte dei linguaggi di sviluppo,
anche quello di scripting della shell è in grado di variare il flusso
delle operazioni da compiere in base a una determinata condizione;
in questo modo gli script possono adattare il proprio funzionamento
ed eseguire interi blocchi di comandi differenti, secondo le necessità.
Questo meccanismo, che assomiglia molto a quello di un interruttore
a due stati, è governato dalla logica booleana, una branca della
matematica nata nel secolo scorso che ha molte applicazioni
pratiche sia in informatica sia in elettronica.
Secondo questa logica, ogni espressione può essere considerata
vera oppure falsa; questi due stati, chiamati valori logici o valori
booleani, sono rappresentati nel framework Microsoft .NET tramite il
tipo System.Boolean e sono disponibili nella shell anche come
variabili automatiche: $true indica il vero mentre $false il falso. Il
recupero del tipo della variabile $true, infatti, ne conferma l’identità:
PS C:\Users\ikmju> $true
True
PS C:\Users\ikmju> $false
False
PS C:\Users\ikmju> 'Efran'.EndsWith('an')
True
Confrontare le espressioni
PowerShell mette a disposizione dell’utente alcuni operatori in grado
di confrontare due distinte espressioni e generare un valore di verità,
in virtù della natura del confronto effettuato.
Tutti gli operatori di questo tipo – chiamati operatori di confronto -
sono preceduti da un trattino (-) e vanno interposti tra le due
espressioni da confrontare, che assumono il nome di operando di
sinistra e operando di destra.
La sintassi è schematizzabile come segue:
Quando uno dei due operandi è $null, infine, l’esito del confronto è
negativo, mentre quando lo sono entrambi l’esito è positivo:
(1 + 2) -ne 3
3 -lt 5
Se uno dei due operandi è pari a $null, infine, l’operatore -lt recupera
il valore di default del tipo dell’operando non nullo e lo utilizza per il
confronto; quando invece entrambi gli operandi sono nulli il confronto
non va mai a buon fine:
(Get-Process).Count -gt 50
Nello script che segue, per esempio, PowerShell ritorna $true solo
quando le espressioni di sinistra hanno lo stesso formato di un
codice di avviamento postale italiano e sono quindi composte da
cinque cifre:
Così come per gli operatori -like e -notlike, anche per -match e -notmatch
valgono le considerazioni già fatte in materia di trattamento di
operandi di tipo differente e operandi nulli.
Il lettore con precedenti esperienze nelle shell *nix può facilmente notare come gli
operatori -match e -notmatch siano un efficiente sostituto del tool a riga di
NO comando grep, mentre il lettore proveniente dalle precedenti shell Windows può
TA constatare come la funzionalità delle espressioni regolari, introdotte con Windows
Scripting Host, abbia finalmente un ruolo di prim’ordine all’interno degli strumenti
offerti dalla shell.
L’operatore -and
L’operatore -and restituisce il valore $true quando entrambe le
espressioni a cui è frapposto sono riconducibili al valore $true (sono,
dunque, vere). Tramite questo operatore logico è quindi possibile
unire due condizioni tra loro in maniera tale da produrne una terza,
che è vera solo quando sono vere entrambe le prime due.
Eseguendo questo blocco, infatti, si ottiene il risultato di verità logica:
L’operatore -or
Questo operatore è simile al precedente, ma ritorna $true non solo
quando entrambi gli operandi sono veri, ma anche nel caso in cui lo
sia solo uno dei due.
Eseguendo questo blocco, quindi, si ottiene, come per l’operatore
precedente, la verità logica:
True
True
L’operatore -xor
L’operatore -xor (acronimo di exclusive or) consente di vagliare due
condizioni e di ritornare $true quando una sola delle due è vera,
$false in tutti gli altri casi. Nonostante sia generalmente meno
utilizzato rispetto ai precedenti, questo operatore permette di creare
condizioni molto complesse in un blocco di codice di dimensioni
ridotte.
L’espressione che segue, di conseguenza, genera il risultato
opposto rispetto ai due operatori analizzati in precedenza:
and):
Infine, questo blocco ritorna $true solo quando esiste il primo file
oppure il secondo, ma non nel caso in cui esistano entrambi oppure
nessuno dei due:
L’operatore -not
Mentre tutti gli operatori analizzati in precedenza utilizzano due
espressioni distinte per generarne una terza, l’operatore -not
ammette un unico operando e genera un valore logico opposto a
quello dell’espressione di riferimento. Se questo operatore è
applicato a un’espressione vera, pertanto, produce un risultato falso;
viceversa, se l’espressione di partenza è falsa, -not restituisce un
risultato vero. Dal punto di vista sintattico questo operatore si colloca
alla sinistra dell’espressione desiderata, diversamente dagli altri
operatori unari: essendocene solo uno, però, quando ci si riferisce
all’operando non lo si qualifica con la posizione assunta.
L’espressione che segue, per esempio, ritorna il valore $true quando
un determinato file non esiste:
I blocchi condizionali
All’interno di PowerShell, così come avviene per la maggior parte dei
linguaggi di sviluppo, è possibile raggruppare una o più istruzioni
affinché siano eseguite solo nel caso in cui una determinata
condizione sia soddisfatta.
Il nome, la sintassi e l’impiego di questi blocchi, chiamati in gergo
blocchi condiziona li, risultano familiari al lettore con precedenti
esperienze di sviluppo nei linguaggi di discendenza C, grazie allo
sforzo profuso da Microsoft per facilitare l’adozione della piattaforma
di scripting della shell da parte del maggior numero possibile di
utenti.
Il blocco if/else/elseif
Il blocco condizionale più elementare, if, prevede l’esecuzione delle
istruzioni contenute al suo interno solo nel caso in cui l’espressione
desiderata risulti pari
a $true.
Questo blocco è composto dalla keyword if seguita dall’espressione
che funge da condizione - racchiusa tra parentesi tonde - e, a
seguire, dal blocco di operazioni da compiere nel caso in cui la
condizione sia soddisfatta, racchiuso tra parentesi graffe. La sintassi
di base di questo blocco è la seguente:
if (<condizione>) {
<elenco comandi>
}
$hour = (Get-Date).Hour
$hour = (Get-Date).Hour
if (<condizione>) {
<elenco comandi>
}
elseif (<condizione>) {
<elenco comandi>
}
elseif (<condizione>) {
<elenco comandi>
}
...
else {
<elenco comandi>
}
Continuando a integrare lo script precedente, quindi, è possibile
aggiungere una serie di keyword elseif all’esempio, in maniera tale
da visualizzare a video messaggi diversi in base all’orario di
esecuzione dello script:
$hour = (Get-Date).Hour
$minute = (Get-Date).Minute
Il blocco switch
Il blocco switch permette di verificare il valore di una particolare
espressione a fronte di una lista di possibilità predefinite dall’utente
ed eseguire l’insieme di istruzioni corrispondente.
Nonostante questo blocco condizionale abbia una semantica e una
sintassi molto simili ai blocchi omonimi presenti nei linguaggi di
derivazione C, PowerShell ne offre una variante ricca di funzionalità
innovative.
Un’istruzione switch di base prevede l’impiego della relativa keyword
e l’indicazione dell’espressione da verificare, seguita dalla lista delle
condizioni da testare, caso per caso, cui sono associati i blocchi di
codice da eseguire nel caso in cui le relative condizioni siano
soddisfatte.
Nell’utilizzo più semplice, un blocco di questo tipo risulta quindi
equivalente a una serie di istruzioni if, una di seguito all’altra, dove
l’operando di sinistra è in comune tra tutte e ogni condizione è un
confronto di uguaglianza rispetto a un’altra particolare espressione.
La sintassi di base del blocco switch è la seguente:
switch (<espressione>) {
<valore> { <elenco comandi> }
<valore> { <elenco comandi> }
<valore> { <elenco comandi> }
...
}
$hour = (Get-Date).Hour
switch ($hour) {
12 { Write-Host 'Ora di pranzo' }
20 { Write-Host 'Ora di cena' }
}
Così come avviene per gli operatori di confronto, anche nel caso di
switch il paragone tra l’espressione da verificare e quella dei diversi
casi avviene facendo il cast di questi ultimi verso il tipo della prima.
Lo script che segue, pertanto, è perfettamente lecito (anche se non
porta ad alcun risultato):
$test = 619
switch ($test) {
235.2 { Write-Host 'Un numero in virgola mobile' }
(Get-Date) { Write-Host 'Una data' }
'xyz' { Write-Host 'Una stringa' }
}
$hour = (Get-Date).Hour
switch ($hour) {
12 { Write-Host 'Ora di pranzo' }
20 { Write-Host 'Ora di cena' }
default { Write-Host 'Ora di studiare PowerShell!' }
}
Anche se la posizione del caso default, rispetto agli altri casi, non varia il risultato
NO
del blocco switch, è preferibile collocare questo caso nell’ultima posizione
TA
disponibile, al fine di aumentare la leggibilità del codice.
-casesensitive:
switch (Get-Date) {
{ ($_.Hour -gè 9) -and ($_.Hour -le 18) } { Write-Host 'Buon lavoro!' }
( ($_.Hour -gt 18) -and ($_.Hour -le 21) } { Write-Host 'Buona serata!' }
( (($_.Hour -gt 7) -and ($_.Minute -gè 30)) -and ($_.Hour -lt 9) } { Write-
Host 'Sveglia!' }
default { Write-Host 'Relax...' }
}
I cicli
Un ciclo, in informatica, consiste in un insieme di comandi eseguiti a
ripetizione, fino al raggiungimento di una determinata condizione; chi
utilizza PowerShell, così come qualsiasi altro linguaggio di scripting,
può trovare nei cicli uno strumento indispensabile per raggiungere il
livello di automazione desiderato all’interno dei propri script.
Il ciclo while
La keyword while introduce il ciclo più elementare disponibile
all’interno della shell, il cui obiettivo consiste nel ripetere un
determinato blocco di comandi fintantoché una particolare
condizione è soddisfatta. La sintassi del ciclo while è rappresentabile
mediante questo schema:
while (<condizione>) {
<elenco comandi>
}
$iterazione = 0
Il ciclo do..while
Questo ciclo si distingue dal precedente per il fatto che la condizione
necessaria per procedere con le iterazioni viene verificata in seguito
all’esecuzione di ogni blocco di istruzioni, anziché in precedenza.
Questa peculiarità porta il blocco di istruzioni a essere sempre
eseguito, pertanto, almeno una volta.
La sintassi del ciclo do..while è rappresentabile nello schema che
segue:
do {
<elenco comandi>
}
while (<condìzione>)
do {
Write-Host 'hello, world'
$iterazione++
}
while ($iterazione -lt 3)
PS C:\Users\ikmju> do {
>> Write-Host 'hello, world'
>> }
>> while ($false)
>>
hello, world
Il ciclo do..until
Il ciclo do..until rappresenta una variante poco utilizzata del ciclo
do..while e consente di invertire la verifica effettuata sulla condizione
del ciclo: ferma restando la prima iterazione, infatti, il blocco di
comandi viene ripetuto solo quando la condizione fornita non è
soddisfatta.
In modo molto simile al precedente, la sintassi di questo ciclo è
riassumibile con questo schema:
do {
<elenco comandi>
}
until (<condizione>)
Lo script che segue, per esempio, visualizza a video una scritta fino
a quando la variabile $iterazione risulta uguale a un determinato
valore:
$iterazione = 0
do {
Write-Host 'hello, world'
$iterazione++
}
until ($iterazione -eq 3)
Il ciclo for
Il ciclo for è una versione più completa del ciclo while e consente di
specificare la condizione, le istruzioni di inizializzazione del blocco e
i comandi da ripetere all’interno di un’unica istruzione.
La sintassi di questo ciclo è illustrata da questo schema:
Il ciclo for è il più versatile tra quelli contemplati dalla shell: se non è
fornita una condizione, infatti, il blocco interno è ripetuto senza limite,
mentre i comandi di ini zializzazione e di iterazione sono opzionali;
peculiarità che rendono questo costrutto l’ideale quando si ha la
necessità di scrivere del codice snello. Nello script che segue, per
esempio, si sfrutta la compattezza del ciclo for per recuperare i
multipli di un intero:
Il ciclo foreach
L’ultimo ciclo illustrato in questo capitolo ha l’obiettivo di facilitare il
recupero di elementi che appartengono a una determinata
collezione. Il ciclo foreach, infatti, consente di snellire la procedura di
esecuzione di un determinato blocco di codice a fronte di ciascun
elemento di un insieme di oggetti.
La sintassi di questo ciclo è semplice e include la definizione della
collezione da utilizzare, della variabile impiegata all’interno del ciclo,
cui abbinare di volta in volta ciascun elemento della collezione, e
infine un elenco di comandi da eseguire a ogni iterazione:
PS C:\Users\ikmju> $i = 0
PS C:\Users\ikmju>
PS C:\Users\ikmju> while ($true) {
>> Write-Host 'Prima'
>>
>> $i++
>> if ($i -eq 3) {
>> break
>> }
>>
>> Write-Host 'Dopo'
>> }
>>
Prima
Dopo
Prima
Dopo
Prima
Se sono presenti più cicli nidificati l’uno nell’altro, sia break sia
continue hanno effetto nell’ambito del ciclo più vicino; per ovviare a
questa limitazione è possibile aggiungere in prima battuta
un’etichetta distintiva ai cicli di interesse, prefissando l’istruzione di
inizio di ciascun ciclo con il simbolo due punti (:) seguito dal testo
desiderato, secondo questo schema:
break <etichetta>
continue <etichetta>
if ($x * $y -eq 9) {
break esterno
}
if ($y -eq 3) {
break
}
$y++
}
}
NO L’utilizzo di etichette all’interno del proprio codice è una pratica che tende a
TA peggiorarne la leggibilità e pertanto se ne sconsiglia l’impiego.
La pipeline
aggregare
Un approfondimento sulla capacità della shell di
i comandi tra loro all’interno della pipeline, studiando
come avviene l’associazione tra oggetti e
parametri e analizzando alcuni tra i cmdlet più
importanti per questa funzionalità.
Aggregare i cmdlet
Quasi tutte le shell moderne sono dotate di un meccanismo in grado
di facilitare il passaggio del risultato di un comando a quello
successivo, come input; si tratta di una soluzione che risponde alla
frequente esigenza di evitare l’impiego di variabili temporanee
all’interno degli script, poiché il più delle volte se ne potrebbe fare a
meno e la loro presenza, alla lunga, rende il codice più difficile da
leggere e da mantenere.
All’interno di CMD, per esempio, è possibile lanciare un qualsiasi
applicativo a console testuale e combinarlo con il comando MORE, in
maniera tale che l’output del primo sia gestito e visualizzato pagina
dopo pagina dal secondo. La combinazione dei due comandi
avviene frapponendo tra il primo e il secondo il carattere pipe ( ι ),
come in questo esempio:
Get-Process cale
I metadati sono informazioni legate agli oggetti, inserite dagli sviluppatori dei
NO
cmdlet e facilmente recuperabili dalla shell; il framework Microsoft.NET dispone di
TA
un avanzato sistema di recupero e gestione dei metadati, basato sugli attributi.
NAME
Stop-Process
[...]
PARAMETERS
[...]
-Id <Int32[]>
Specifies the process IDs of the processes to be stopped. To specify
multiple IDs, use commas to separate the IDs. To find the PID of a process, type
"get-process". The parameter name ("Id") is optional.
Required? true
Position? 1
Default value
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? false
-InputObject <Process[]>
Stops the processes represented by the specified process objects. Enter
a variable that contains the objects, or type a command or expression that gets the
objects.
Required? true
Position? named
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
-Name <string[]>
Specifies the process names of the processes to be stopped. You can type
multiple process names (separated by commas) or use wildcard characters.
Required? true
Position? named
Default value
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? False
Per determinare se un parametro ammette il passaggio di oggetti
dalla pipeline, quindi, è sufficiente verificare tramite Get-Help (o
consultando la guida in formato .CHM, disponibile con PowerShell
ISE) se alla relativa voce Accept pipeline input? corrisponde il valore true:
in tal caso il sistema visualizza, a seguire, la modalità di passaggio
consentita (ßyValue oppure ByPropertyName).
Come si evince dall’output della guida, dunque, il cmdlet stop-process
ammette di essere alimentato tramite il passaggio di oggetti
provenienti dalla pipeline a patto che questi rispettino almeno una di
queste condizioni:
• siano oggetti di tipo Process o collezioni di questi ultimi oppure
oggetti convertibili in questo tipo;
• abbiano una proprietà id di tipo intero (o una collezione di interi)
oppure di un tipo convertibile in intero (o collezione);
• abbiano una proprietà Name di tipo stringa (o una collezione di
stringhe) oppure di un tipo convertibile in stringa (o collezione).
È possibile, d’altra parte, che l’oggetto fornito dalla pipeline soddisfi
più di uno di questi criteri: gli stessi oggetti ritornati da Get-Process,
infatti, li soddisfano tutti! Ognuno di questi è di tipo Process e
dispone sia della proprietà id sia della proprietà Name:
Get-TraceSource.
La pipeline in azione
Tutti i cmdlet analizzati fino a questo punto del libro supportano la
pipeline e possono impiegare questa tecnologia per creare potenti
aggregati di comandi; alcuni cmdlet, d’altra parte, sono nati quasi
esclusivamente per essere impiegati all’interno della pipeline ed è
davvero raro osservarne qualcuno al di fuori. Si tratta per lo più di
cmdlet generici, in grado di interagire con qualsiasi tipo di oggetto e
di fungere da collettori tra i blocchi della pipeline.
Data la natura di questi cmdlet, molti di essi sono in grado di
interagire agevolmente con le collezioni e gli array di oggetti,
argomento approfondito nel Capitolo 10.
Where-Object
where-object consente di creare un filtro all’interno della pipeline tra il
Get-Process |
Where-Object { $_.StartTime -gt (Get-Date).AddHours(-2) } |
Sort-Object WorkingSet -Descending
Select-Object
Select-Object è un cmdlet che permette di effettuare proiezioni ed
estrazioni dei valori che provengono dalla pipeline.
Questo comando è in grado di limitare il numero di oggetti ritornati
tramite la pipeline; fornendo un valore al parametro -Skip, infatti,
Select-Object ignora il numero di elementi specificato, ritornandone
Get-Process |
Where-Object { $_.StartTime -gt (Get-Date)-AddHours(-2) } |
Sort-Object WorkingSet -Descending
Select-Object -First 1
Description StartTime
----------- ---------
Apple Mobile Device Service 2/11/2010 9:33:12 AM
AMD External Events Client Module 2/11/2010 9:33:11 AM
AMD External Events Service Module 2/11/2010 9:33:09 AM
Windows Calculator 2/11/2010 11:13:08 AM
I nuovi oggetti, d’altra parte, non hanno più alcun legame con il tipo
di oggetto di partenza; il nuovo tipo creato ad hoc, infatti, appartiene
al namespace fittizio
Selected:
PS C:\Users\ikmju> Get-Process
>> Sort-Object WorkingSet -Descending
>> Select-Object -First 1 -ExpandProperty Modules
>>
ForEach-Object
Il cmdlet ForEach-object consente di eseguire in pochissimo codice un
determinato blocco di istruzioni per ogni oggetto fornito tramite
pipeline. A parte il valore associato generalmente tramite pipeline, il
comando utilizza il parametro -process (unico posizionale) per
specificare un blocco di istruzioni da eseguire a ogni iterazione; è
prevista, inoltre, l’indicazione di un blocco di istruzioni da eseguire
prima delle iterazioni tramite il parametro -Begin e un blocco da
eseguire al termine, mediante il parametro -End. All’interno del blocco
specificato tramite -process il sistema consente il recupero dell’oggetto
fornito tramite la pipeline mediante la variabile automatica $_.
La sintassi di questo cmdlet è riassumibile secondo questo schema:
Al posto del nome del cmdlet è frequente utilizzare uno dei due alias:
foreach e il carattere percentuale (%). Anche in questo caso tra i due è
preferibile utilizzare il secondo, che rende il codice meno prolisso e
ha una forma che ricorda in qualche maniera l’obiettivo del
comando.
Facendo uso di ForEach-Object e della capacità di memorizzare i dati su
file fornita dal cmdlet Set-Content, approfondito nel Capitolo 20, è
possibile creare uno script che richiede la lista dei processi in
esecuzione in una macchina remota e, per ciascuno di essi,
recupera il nome e il percorso dell’eseguibile principale, salvando poi
il tutto in un file di testo in locale:
$log = ''
Get-Process -ComputerName -
ForEach-Object { $log += $_.MainModule-FileName + [Environment]::NewLine
} -Begin { $log = '' } -End { $log }
Set-Content ProcessLog.txt
Group-Object
Il cmdlet Group-object, infine, consente di raggruppare gli oggetti che
provengono dalla pipeline in base a una o più proprietà; una volta
effettuata l’elaborazione, il comando ritorna alla pipeline un oggetto
che rappresenta il gruppo per ogni insieme trovato. Nell’utilizzo di
base, ognuno di questi gruppi è un’istanza di Grouplnfo, un tipo che
permette di recuperare la collezione degli elementi che formano il
gruppo (tramite la proprietà Group), il numero di tali elementi (tramite la
proprietà count), i valori delle proprietà che individuano il gruppo
(grazie alla proprietà values) e la rappresentazione testuale di questi
ultimi (mediante la proprietà Name). La sintassi di base è
rappresentabile da questo schema:
La visualizzazione
La peculiarità principale di PowerShell, come si è potuto apprendere
nel corso del libro, è quella di essere una shell orientata agli oggetti;
questa caratteristica, naturalmente, implica che l’esecuzione di
qualsiasi espressione all’interno di questa piattaforma genera, infine,
uno o più oggetti del framework Microsoft.NET.
Sia l’interfaccia a console sia l’ISE di PowerShell e quasi tutte le
interfacce alternative non prodotte da Microsoft, d’altra parte, sono in
grado di visualizzare l’output generato dalla shell impiegando solo
del testo; non sarebbe possibile, infatti, riuscire a prevedere il
funzionamento e la complessità degli oggetti impiegati e utilizzare, di
conseguenza, rappresentazioni differenti per ciascuna tipologia.
Raffigurare un numero o una data (o, banalmente, una stringa) può
sembrare un’attività semplice, ma cercare di “visualizzare” un
oggetto astratto come un processo, per esempio, porterebbe a
risultati parziali o inconcludenti.
PS C:\Users\ikmju> 'powershell.it'
powershell.it
Esiste una quarta visualizzazione, di tipo ibrido, la quale genera un testo che
racchiude le informazioni secondo uno schema che ricorda quello usato per
serializzare i dati tramite il protocollo JSON (probabilmente noto al lettore con
NO
esperienze di sviluppo in ambito web), dove il sistema si occupa di recuperare
TA
quante più informazioni possibili per ogni proprietà. L’impiego di questa modalità di
visualizzazione è assai raro, pertanto il seguito del libro non la prende in
considerazione.
Format-Table
Il cmdlet Format-Table è sicuramente il più utilizzato nella categoria dei
comandi di formattazione e consente di generare una
visualizzazione a tabella per gli elementi che gli vengono forniti.
Dispone di un parametro -InputObject cui è possibile fornire l’oggetto
da visualizzare, anche se è di norma preferibile sfruttare la pipeline;
il parametro -Property (unico posizionale), poi, consente di indicare i
nomi delle proprietà dell’oggetto di input da visualizzare, separandoli
tramite il carattere virgola (,). La larghezza della tabella generata è
determinata in base al numero delle colonne ma, specificando lo
switch -AutoSize, il cmdlet è in grado di adattarla automaticamente alla
lunghezza massima dei testi che ciascuna cella è chiamata a
contenere.
Quando un testo relativo a un valore di una proprietà è troppo lungo,
poi, viene automaticamente troncato e specificando lo switch -Wrap è
possibile fare in modo che le righe della tabella possano occupare
più di una riga fisica della console testuale. Lo switch -HideTableHeaders,
infine, permette di disattivare la visualizzazione delle intestazioni di
colonna, di default visibili. La sintassi di base per questo cmdlet è:
Per ottenere una vista in formato tabella con le informazioni sui nomi
e sullo stato dei servizi attivi, per esempio, è quindi possibile
sfruttare questo script:
PS C:\Users\ikmju> Get-Service |
>> ? { $_.Status -eq 'Running' } |
>> Format-Table Name,Status
>>
Name Status
---- ------
AMD External Events Utility Running
AppHostSvc Running
Apple Mobile Device Running
[...]
PS C:\Users\ikmju> Get-Service |
>> ? { $_.Status -eq 'Running' } |
>> Format-Table Name,Status -AutoSize
>>
Name Status
---- ------
AMD External Events Utility Running
AppHostSvc Running
Apple Mobile Device Running
AudioEndpointBuilder Running
[...]
PS C:\Users\ikmju> Get-Service |
>> Sort Status |
>> Format-Table Name, Status -GroupBy Status -AutoSize
>>
Status: Stopped
Name Status
---- ------
RpcLocator Stopped
SCardSvr Stopped
SCPolicySvc Stopped
ReportServer Stopped
[...]
Status: Running
Name Status
---- ------
wcncsvc Running
Netman Running
CertPropSvc Running
BITS Running
W3SVC Running
[...]
Format-Wide
Questo cmdlet visualizza tramite una tabella estesa gli oggetti che gli
vengono forniti; si tratta, tipicamente, di una valida alternativa
all’impiego di Format-Table quando ciò che si desidera rappresentare a
video consiste in un’unica proprietà di una serie di oggetti.
Format-Wide condivide con il cmdlet precedente la maggior parte dei
PS C:\Users\ikmju> Get-Service |
>> ? { $_.Status -eq 'Running' }
>> Format-Wide Name -AutoSize
>>
PS C:\Users\ikmju> Get-Service |
>> Sort Status |
>> Format-Wide Name -AutoSize -GroupBy Status
>>
Status: Stopped
Status: Running
Format-List
Il cmdlet Format-List permette di rappresentare un oggetto come una
lista di proprietà dello stesso, dove ogni riga corrisponde a una
coppia proprietà–valore; poiché lo spazio adibito a contenere
ciascun valore è abbastanza elevato, in rapporto alla larghezza in
caratteri della finestra della console, si corre meno il rischio che tali
valori siano troncati. Questa visualizzazione, che assomiglia per certi
versi a una rotazione della visualizzazione a tabella, può in
determinati casi diventare più agevole. Come i due cmdlet
precedenti, anche Format-List consente di indicare l’oggetto desiderato
tramite il parametro -InputObject: anche in questo caso, tuttavia, è
generalmente preferibile impiegare la pipeline. Il solito parametro -
Property, poi, consente di indicare le proprietà da visualizzare,
Name : AeLookupSvc
Status : Stopped
Name : ALG
Status : Stopped
Name : AMD External Events Utility
Status : Running
[...]
PS C:\Users\ikmju> Get-Service |
>> Sort-Object Status |
>> Format-List Name -GroupBy Status
>>
Status: Stopped
Name : ReportServer
Name : RpcLocator
Name : SCardSvr
[...]
Status: Running
Name : CertPropSvc
Name : LanmanWorkstation
[...]
Viste predefinite
Nell’ottica di rendere il sistema di visualizzazione degli oggetti il più
semplice ed efficiente possibile, PowerShell si avvale di una serie di
viste predefinite, che consentono di esplicitare sia il tipo di
visualizzazione desiderato (tra quelli illustrati in precedenza) sia le
proprietà interessate, per ciascun tipo di oggetto del framework
Microsoft.NET.
Ogni vista predefinita ha un nome, che si può utilizzare con uno
qualsiasi dei cmdlet di formattazione: Format-Table, Format-Wide e Format-
List, infatti, dispongono di un ulteriore parametro -view, tramite il quale
Directory: C:\Windows\System32\WindowsPowerShell\v1.0
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 16.7.2009 19:21 27338 Certificate.format.ps1xml
-a--- 16.7.2009 19:21 27106 Diagnostics.Format.ps1xml
-a--- 16.7.2009 19:21 72654 DotNetTypes.format.ps1xml
-a--- 16.7.2009 19:21 24857 FileSystem.format.ps1xml
-a--- 16.7.2009 19:21 257847 Help.format.ps1xml
-a--- 16.7.2009 19:21 89703 PowerShellCore.format.ps1xml
-a--- 16.7.2009 19:21 18612 PowerShellTrace.format.ps1xml
-a--- 16.7.2009 19:21 20120 Registry.format.ps1xml
-a--- 16.7.2009 19:21 24498 WSMan.Format.ps1xml
<View>
<Name>NomeVistaPredefinita</Name>
<ViewSelectedBy>
<TypeName>Namespace.NomeTipo</TypeName>
</ViewSelectedBy>
[<TableControl>...</TableControl>]
[<WideControl>...</WideControl>]
[<ListControl>...</ListControl>]
[<GroupBy>...</GroupBy>]
</View>
<View>
<Name>process</Name>
<ViewSelectedBy>
<TypeName>System.Diagnostics.Process</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Label>Handles</Label>
<Width>7</Width>
<Alignment>right</Alignment>
</TableColumnHeader> - - -
[...]
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>HandleCount</PropertyName>
</TableColumnItem>
[...]
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>Priority</Name>
<ViewSelectedBy>
<TypeName>System.Diagnostics.Process</TypeName>
</ViewSelectedBy>
<GroupBy>
<PropertyName>PriorityClass</PropertyName>
<Label>PriorityClass</Label>
</GroupBy>
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Width>20</Width>
</TableColumnHeader>
[...]
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>ProcessName</PropertyName>
</TableColumnItem>
[...]
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>process</Name>
<ViewSelectedBy>
<TypeName>System.Diagnostics -Process</TypeName>
</ViewSelectedBy>
<WideControl>
<WideEntries>
<WideEntry>
<WideItem>
<PropertyName>ProcessName</PropertyName>
</WideItem>
</WideEntry>
</WideEntries>
</WideControl>
</View>
errore. Volendo fare uso della vista predefinita priority, per esempio,
è possibile eseguire questo blocco di codice:
PS C:\Users\ikmju> Get-Process |
>> Sort PriorityClass
>> Format-Table -View Priority
>>
PriorityClass: High
ProcessName Id HandleCount WorkingSet
----------- -- ----------- ----------
winlogon 668 113 1048576
wininit 528 79 745472
dwm 1664 130 44453888
[...]
PS C:\Users\ikmju> ps explorer | ft
PS C:\Users\ikmju> ps explorer | fw
explorer
Proprietà predefinite
Il lettore più accorto potrebbe aver notato che tra le viste predefinite
illustrate in precedenza per system.Diagnostics.process non ne appare
nessuna associata alla visualizzazione a lista (Listcontrol);
richiamando Format-List, d’altra parte, si visualizza solo una quota
parte delle proprietà dei processi e non tutte, come forse ci si
potrebbe aspettare.
PS C:\Users\ikmju> ps explorer | fi
Id : 652
Handles : 477
CPU :12.4176796
Name : explorer
<Type>
<Name>System.Diagnostics -Process</Name>
<Members>
<MemberSet>
<Name>PSStandardMembers</Name>
<Members>
<PropertySet>
<Name>DefaultDisplayPropertySet</Name>
<ReferencedProperties>
<Name>Id</Name>
<Name>Handles</Name>
<Name>CPU</Name>
<Name>Name</Name>
</ReferencedProperties>
</PropertySet>
</Members>
</MemberSet>
[...]
</Members>
</Type>
L’ultimo caso
Quando il tipo da visualizzare non è dotato di proprietà predefinite né
esiste alcuna vista predefinita per la visualizzazione desiderata, i
cmdlet di formattazione sopperiscono alla mancanza dell’indicazione
delle proprietà da visualizzare in maniera differente:
• Format-Table visualizza le prime dieci proprietà dell’oggetto;
• Format-Wide impiega la prima proprietà dell’oggetto;
• Format-List utilizza tutte le proprietà dell’oggetto.
Una volta compreso il meccanismo di formattazione degli oggetti è
finalmente possibile fare un passo indietro e studiare come questo
processo sia correlato alla pipeline e alla consumazione degli
oggetti.
Consumare l’output
Come anticipato all’inizio del capitolo, la visualizzazione all’interno
dell’interfaccia dell’host non è l’unica modalità di consumazione degli
oggetti provenienti dalla pipeline; PowerShell dispone, infatti, di una
serie di cmdlet creati per sfruttarne l’output. Questi cmdlet, che per
convenzione sono dotati del verbo out, se inseriti all’interno della
pipeline consumano tutti gli oggetti generati dai blocchi che li
precedono, per acquisirne una rappresentazione testuale da inviare
alla periferica di output di competenza per ciascun comando.
Poiché i cmdlet di output non emettono, a loro volta, alcun oggetto
all’interno della pipeline, eventuali blocchi che li succedono sono
ignorati dalla shell: di norma, pertanto, sono sempre posti come
ultimo blocco delle pipeline.
Se si apprestano a consumare delle stringhe o collezioni di stringhe,
i cmdlet di output non elaborano ulteriormente questi oggetti, ma si
limitano a inviarli alla periferica di output di competenza; in caso
contrario, la shell impiega preventivamente uno dei cmdlet di
formattazione per generare la rappresentazione testuale dell’output
della pipeline. Il cmdlet di formattazione utilizzato è determinato in
base al primo oggetto emesso dalla pipeline: se questo contiene più
di cinque proprietà viene usato Format-List, altrimenti il cmdlet
prescelto è Format-Table. In entrambi i casi, in ogni modo, i cmdlet sono
richiamati senza esplicitare né proprietà né viste predefinite e la shell
si avvale, dunque, delle tecniche esposte nei paragrafi precedenti.
Out-Host
Il cmdlet Out-Host invia la rappresentazione testuale dell’output della
pipeline all’interfaccia host di PowerShell, per la visualizzazione.
Si tratta senz’altro del cmdlet più utilizzato all’interno di PowerShell,
perché il sistema lo impiega in maniera predefinita per consumare
l’output dei comandi. Al termine della pipeline, infatti, se la shell
rileva che sono stati emessi degli oggetti ma nessun blocco li ha
consumati, esegue out-Host passandoglieli. Tutte le pipeline che
emettono oggetti, ma non contengono cmdlet di output, dunque,
utilizzano in maniera trasparente questo cmdlet. I due blocchi che
seguono, di conseguenza, sono identici:
Out-GrìdVìew
Introdotto con la versione 2.0 di PowerShell, il potente cmdlet out-
Gridview consente di rappresentare a video, all’interno di una griglia
interattiva, gli oggetti emessi dalla pipeline. Unico nel suo genere,
questo comando fornisce un’utilissima alternativa alla
visualizzazione testuale perché la griglia prodotta consente di
manipolare molto più agevolmente l’output.
Il cmdlet, richiamabile anche tramite l’alias predefinito ogv, dispone di
un unico parametro -Title, tramite il quale è possibile impostare il
testo del titolo della griglia.
La sintassi è rappresentata da questo semplice schema:
... | Out-GridView [-Title <Titolo>]
Out-String
Questo cmdlet è in grado di “catturare” l’output di una pipeline
all’interno di una stringa. Di default, il comando produce un’unica
stringa, ma è possibile specificare lo switch -Stream per ottenere una
collezione di stringhe, una per ciascuna riga prodotta.
Il parametro -Width consente, infine, di impostare la larghezza delle
righe elaborate dal comando, che per l’host di PowerShell è pari a
80 (la finestra a console, infatti, ha storicamente una dimensione di
80 caratteri in larghezza per 25 in altezza). La sintassi del comando
è:
... | Out-String [-Width <Larghezza>]
Out-Null
Il cmdlet Out-Null consuma gli oggetti provenienti dalla pipeline, ma
non li invia ad alcuna periferica di output. Questo comando è
tipicamente impiegato per sopprimere la visualizzazione dell’output
da parte di Out-Host per quei blocchi di comando che non ne hanno
bisogno. La sintassi è davvero semplice:
... | Out-Null
Il blocco che segue, per esempio, non produce alcun output a video
perché gli oggetti emessi dalla pipeline nel primo blocco sono
consumati interamente nel secondo, quindi la shell non richiama Out-
Host:
PS C:\Users\ikmju> ps | Out-Null
Out-File
Il cmdlet Out-File, il cui obiettivo consiste nel dirottare l’output di una
pipeline all’interno di un file, è approfondito nel Capitolo 20, dedicato
alla manipolazione dei file.
Parte III
Elaborazione dei dati
Gli array
La creazione
È sufficiente un elenco di espressioni separate da virgola per
generare automaticamente un nuovo array: le espressioni
dell’elenco diventano gli elementi di quest’ultimo, conservandone
l’ordine di partenza.
Definire un nuovo array composto da tre elementi è dunque
semplice come eseguire questo blocco:
$test = 9, 7, 83
PS C:\Users\ikmju> $test.GetType()
La visualizzazione
La shell permette di visualizzare a video il contenuto di un qualsiasi
array, digitandone semplicemente il nome alla riga del prompt:
PS C:\Users\ikmju> $test
9
7
33
PS C:\Users\ikmju> $test
123
powershell.it
PS C:\Users\ikmju> $test
123
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ----- ----- ----- ----- ------ -- -----------
33 2 1536 3136 35 1268 conime
86 3 2512 4980 55 0,07 2012 wuauclt
456
Come il lettore più attento potrebbe aver notato, gli elementi ritornati
da Get-Process utilizzano la visualizzazione a tabella, di default per
questo tipo di oggetti. Utilizzando Format-List il risultato cambia, come
ci si aspetterebbe:
Id : 1268
Handles : 33
CPU :
Name : conime
Id : 3716
Handles : 86
CPU : 0,1101584
Name : wuauclt
456
PS C:\Users\ikmju> (Get-Process).GetType()
PS C:\Users\ikmju> $test = 9, 7, 83
PS C:\Users\ikmju> $test[0]
9
PS C:\Users\ikmju> $test[2]
83
PS C:\Users\ikmju> $test[-3]
9
PS C:\Users\ikmju> $test[-2]
7
$test[0] = 123
PS C:\Users\ikmju> 5.-7
5
6
7
PS C:\Users\ikmju> $a = 3, 4, 5
PS C:\Users\ikmju> $b = 1, 2, 3
PS C:\Users\ikmju>
PS C:\Users\ikmju> $a + $b
3
4
5
1
2
3
PS C:\Users\ikmju> $a + $b + 7
3
4
5
1
2
3
7
Rimuovere elementi
Non esiste una via diretta, purtroppo, per eliminare gli elementi di un
determinato array; in questo caso è necessario creare un nuovo
array in maniera tale che contenga tutti gli elementi del primo
all’infuori di quello da eliminare. D’altra parte, le tecniche di recupero
delle sezioni di array analizzate in precedenza rendono questo
compito molto semplice. Lo script che segue mostra come usufruire
dell’operatore di range numerico per rimuovere dall’array di esempio
il quarto elemento:
Lo splitting
All’interno della shell è possibile separare gli elementi di un array
utilizzando una tecnica nota come splitting. Con questo sistema si
utilizza l’operatore di assegnazione, impiegando come operando di
sinistra una serie di variabili separate dal simbolo virgola (, ), e come
operando di destra l’array desiderato: da questo PowerShell estrae
tanti elementi quante sono le variabili della serie di sinistra, meno
uno, e ne utilizza i valori per impostarle. All’ultima variabile
dell’operando di sinistra, infine, vengono associati gli elementi
rimasti: se sono più di uno la variabile risultante è un array, in caso
contrario no.
La sintassi d’uso di questa tecnica è schematizzata di seguito:
$a, $b, ..., $n = $array
L’esempio che segue estrae i primi due elementi dell’array in due
variabili distinte (Sfirst e $second) e la sezione rimanente in un secondo
array ($others):
PS C:\Users\ikmju> $test = 1, 2, 3, 4, 5, 6, 7
PS C:\Users\ikmju> $first, $second, $others = $test
PS C:\Users\ikmju>
PS C:\Users\ikmju> $first
1
PS C:\Users\ikmju> $second
2
PS C:\Users\ikmju> $others
3
4
5
5
7
PS C:\Users\ikmju> $test = 1, 2, 3, 4, 5, 6, 7
PS C:\Users\ikmju> $first, $null, $third, $null = $test
PS C:\Users\ikmju> $first
1
PS C:\Users\ikmju> $third
3
PS C:\Users\ikmju> ©(Get-Process).Count
75
PS C:\Users\ikmju> ©(Get-Process System).Count
1
PS C:\Users\ikmju> ©().Count
0
Nello script che segue, per esempio, l’array $test viene filtrato e sono
ritornati solo gli elementi minori di un particolare valore:
PS C:\Users\ikmju> $test = 1, 2, 3, 4, 5, 6, 7
PS C:\Users\ikmju> $test -lt 3
1
2
if ($element % 2 -eq 0) {
$element
}
}
PS C:\Users\ikmju> $test = 1, 2, 3, 4, 5, 6, 7
PS C:\Users\ikmju> $test -contains 3
True
PS C:\Users\ikmju> $test -contains 8
False
Poiché $null è un valore come tutti gli altri, infine, questi operatori si
comportano come ci si aspetterebbe:
Metodi di rilievo
Come anticipato all’inizio del capitolo, ogni array è un’istanza della
classe System. Array (o di una sua derivata) e, come tale, può usufruire
di una serie di metodi e proprietà che rendono più agevole l’impiego
di questi oggetti. Nonostante ne esistano alcuni a livello d’istanza
(come la proprietà Length, già menzionata), i membri più interessanti
di questo tipo sono statici. Per ottenere un elenco dei membri statici
degli array è sufficiente utilizzare lo switch -Static di Get-Member:
Metodo Index0f()
Questo metodo è in grado di vagliare l’array fornito, elemento per
elemento, alla ricerca di un determinato valore. Alla prima
occorrenza trovata il metodo termina l’elaborazione ritornando il
relativo indice; nel caso non ne venga trovata alcuna, il metodo
restituisce il valore -1.
La sintassi di questo metodo è la seguente:
[array]::IndexOf(<Array>, <Valore>)
PS C:\Users\ikmju> $test = 6, 3, 5, 2, 4, 1
PS C:\Users\ikmju> [array]::IndexOf($test, 2)
3
PS C:\Users\ikmju> [array]::IndexOf($test, 7)
-1
PS C:\Users\ikmju> $test = 6, 3, 5, 6, 5, 6
PS C:\Users\ikmju> [array]::IndexOf($test, 6, 2)
3
Metodo LastlndexOf()
Il metodo LastlndexOf() agisce in maniera speculare a IndexOf(),
effettuando la ricerca degli elementi a ritroso, partendo dalla fine.
Al pari di quest’ultimo, il metodo dispone di un overload tramite il
quale è possibile indicare l’indice di partenza.
Le sintassi d’uso per i due sono queste:
[array]::LastIndexOf(<Array>, <Valore>)
[array]::LastlndexOf(<Array>, <Valore>, <Partenza>)
PS C:\Users\ikmju> $test = 6, 3, 5, 6, 5, 6
PS C:\Users\ikmju> [array]::LastlndexOf($test, 6)
5
PS C:\Users\ikmju> [array]::LastlndexOf($test, 6, 4)
3
Metodo Reverse()
Il metodo Reverse() inverte la sequenza degli elementi all’interno di un
array. Dispone anche di un overload cui è possibile specificare un
indice di partenza e un numero di elementi da processare, in
maniera tale che questa operazione sia limitata a una porzione
dell’array.
Le sintassi d’uso per i due overload sono:
[array]::Reverse(<Array>)
[array]::Reverse(<Array>, <Partenza>, <NumeroElementi>;
Nel blocco che segue, per esempio, la sequenza dei primi cinque
numeri naturali viene invertita dal comando:
PS C:\Users\ikmju> $test = 1, 2, 3, 4, 5
PS C:\Users\ikmju> [array]::Reverse($test)
PS C:\Users\ikmju> $test
5
4
3
2
1
PS C:\Users\ikmju> $test = 1, 2, 3, 4, 5
PS C:\Users\ikmju> [array]::Reverse($test, 2, 3)
PS C:\Users\ikmju> $test
1
2
5
4
3
Gli array associativi
Come è stato illustrato nel capitolo precedente, gli array sono valide
strutture di cui avvalersi all’interno del proprio codice per ospitare ed
elaborare informazioni e dati. Il framework Microsoft .NET, così come
molti altri ambienti di sviluppo, consente di utilizzare un tipo simile al
precedente ma dotato di caratteristiche e funzionalità aggiuntive,
chiamato array associativo (oppure tabella hash). La differenza
principale tra questi due tipi consiste nel fatto che negli array ogni
elemento è individuabile unicamente tramite un indice numerico,
mentre negli array associativi l’indice può essere, esso stesso, di un
tipo qualsiasi. Questa maggiore libertà di espressione permette agli
array associativi di essere estremamente utili in tutte quelle
situazioni dove si ha la necessità di creare correlazioni tra alcuni
oggetti e altri e, successivamente, poterle recuperare e gestire
agevolmente.
La creazione
Per creare un array associativo all’interno di PowerShell è sufficiente
utilizzare un costrutto simile a quello impiegato per l’operatore di
sottoespressione di array, con la differenza che, anziché far seguire
al simbolo di a commerciale (@) delle parentesi tonde, sono usate
due parentesi graffe: il risultato di questa operazione è un’istanza del
tipo System.Collections.Hashtable.
La creazione di questi oggetti fa capo, quindi, allo schema sintattico
che segue:
$hash = @{}
La visualizzazione
La shell rappresenta gli array associativi utilizzando, di default, una
visualizzazione a tabella: una prima colonna riporta l’intestazione Name
e contiene tutte le chiavi utilizzate dall’oggetto. La seconda colonna,
invece, contiene i valori a cui puntano le chiavi e riporta
l’intestazione Value. Per visualizzare il contenuto di un array
associativo è sufficiente digitarne il nome al prompt:
Name Value
---- -----
Argon 18
Neon 10
Elio 2
Name Value
---- -----
2/23/2010 11:37:38 AM 123
System System.Diagnostics.Process (System)
100 Cento
Alla stregua di quanto avviene per gli array, anche gli array
associativi dispongono della proprietà Count, tramite la quale è
possibile risalire al numero di correlazioni memorizzate all’interno di
ciascun oggetto. Nello script che segue, infatti, il sistema riporta il
valore desiderato:
<Array associativo>[<Chiave>]
L’operatore punto
Poiché gli array associativi sono tra i tipi più impiegati all’interno
della shell, il sistema consente di recuperarne gli elementi mediante
una sintassi alternativa basata sull’operatore punto, molto simile a
quella impiegata per ricavare i valori delle proprietà di un oggetto.
Se le chiavi sono specificate come stringhe prive di spazi, infatti, i
valori a esse associati sono ottenibili seguendo uno schema simile a
questo:
<Array associativo>.<Chiave>
PS C:\Users\ikmju> $nobleGases.Kripton = 36
PS C:\Users\ikmju> $nobleGases
Name Value
---- -----
Argon 18
Neon 10
Elio 2
Kripton 36
Rimuovere elementi
Per rimuovere una correlazione all’interno di un array associativo è
necessario impiegare il metodo Remove(), cui è necessario fornire la
chiave desiderata. La sintassi di questo metodo è la seguente:
<Array associativo>.Remove(<Chiave>)
PS C:\Users\ikmju> $nobleGases.Remove('Kripton')
ConvertFrom-StringData
A partire dalla versione 2.0, PowerShell include il nuovo cmdlet
ConvertFrom-StringData, dedicato alla creazione di array associativi a
partire da una rappresentazione testuale degli stessi.
Per utilizzare questo comando è necessario specificare, per mezzo
del parametro-StringData (unico posizionale), una stringa formata da
tante righe quante sono le correlazioni volute. Ciascuna riga deve
essere composta seguendo uno schema predefinito, che include il
nome della chiave desiderata, il simbolo di assegnazione (=) e la
rappresentazione testuale dell’espressione associata, secondo
questo schema:
<Chiave> = <Valore>
[<Chiave> = <Valore>]
...
SRV01 = 10.0.1.1
SRV02 = 10.0.1.2
SRV03 = 10.0.1.3
Proprietà Keys
Questa proprietà consente di recuperare la collezione delle chiavi
utilizzate dall’array associativo.
In questo script, per esempio, sono visualizzate a video le chiavi
delle correlazioni create in fase di inizializzazione di un array
associativo:
Proprietà Values
Alla stregua della precedente, la proprietà Values permette di
recuperare la collezione dei valori impiegati dall’array associativo:
PS C:\Users\ikmju> $nobleGases.Values
18
10
2
Metodo GetEnumerator()
Questo metodo consente di recuperare la collezione di correlazioni
memorizzate internamente all’array associativo. Nonostante la shell
possa far presupporre il contrario, infatti, un array associativo non è
un array, pertanto non c’è modo di farne fluire automaticamente gli
elementi all’interno della pipeline. Il lettore più smaliziato può aver
provato, probabilmente, a fornire tramite pipeline un array
associativo a un cmdlet, come per esempio Sort-Object. In tal caso il
risultato non è quello atteso e l’ordinamento degli oggetti sembra
non funzionare a dovere:
Name Value
---- -----
Argon 18
Neon 10
Elio 2
Name Value
---- -----
Argon 18
Elio 2
Neon 10
Metodo ContainsKey()
Il metodo ContainsKey() verifica se un particolare oggetto è presente
all’interno delle chiavi dell’array associativo e ritorna il valore $true in
caso affermativo, $false in caso contrario. Accetta, pertanto, un unico
parametro che rappresenta la chiave da verificare; ecco la sintassi di
chiamata:
<Array associativo>.ContainsKey(<Chiave>)
PS C:\Users\ikmju> $nobleGases.ContainsKey('Neon')
True
PS C:\Users\ikmju> $nobleGases.ContainsKey('Polonio')
False
Metodo ContaìnsValue()
Il metodo ContainsValue() verifica se un particolare oggetto è
presente tra i valori delle correlazioni di un array associativo. In
maniera identica a ContainsKey(), infatti, ritorna $true se l’oggetto fornito
è tra questi ultimi, $false altrimenti. La sintassi di chiamata è questa:
<Array associativo>.ContainsValue(<Valore>)
PS C:\Users\ikmju> $nobleGases.ContainsValue(2)
True
PS C:\Users\ikmju> $nobleGases.ContainsValue(3)
False
Le stringhe
La creazione
PowerShell consente di creare nuove stringhe secondo modalità
differenti, in base alle esigenze; quando il concetto di stringa è stato
introdotto, nel Capitolo 3, si è scelto di illustrare il sistema più
semplice, che prevede di racchiudere i caratteri che compongono il
testo desiderato tra apici singoli ('). Questo costrutto prende il nome
di stringa letterale (literal string) e segue dunque questo semplice
schema:
<Testo>
PS C:\Users\ikmju> "powershell.it".Length
13
PS C:\Users\ikmju> "9*7=$( 9*7 (".Length
6
Il carattere di escape
All’interno delle stringhe definite tramite doppi apici, PowerShell
attribuisce al carattere di apice inverso (`), noto anche come
backtick, un significato particolare: quando questo simbolo precede
quello del dollaro ($) il sistema non sostituisce il riferimento
all’eventuale variabile o espressione da questo introdotta,
mantenendo il testo così com’è. Si tratta, in sostanza, di un
“allontanamento” momentaneo rispetto alla normale attività di
elaborazione delle stringhe, un concetto che ha portato a dare
all’apice inverso il nome di carattere di escape.
NO Il carattere di apice inverso non è purtroppo presente nel layout italiano di tastiera
TA ed è pertanto necessario utilizzare la combinazione di tasti ALT+96 per ottenerlo.
PS C:\Users\ikmju> $x = 123
PS C:\Users\ikmju> "$x, `$x, ``$x"
123, $x, `123
Le sequenze di escape
L’apice inverso ha un’ulteriore funzione all’interno delle definizioni di
stringhe effettuate tramite doppi apici: quando incontra questo
simbolo seguito da un particolare carattere tra quelli previsti, la shell
identifica la combinazione come una sequenza di escape e la
sostituisce con i caratteri speciali corrispondenti.
Tabella 12.1 - Le sequenze di escape.
Sequenza Caratteri speciali
`n Nuova riga
`t Tabulazione orizzontale
`r Ritorno a capo
`f Avanzamento di linea
`b Backspace
`o $null (raramente utilizzato)
`a Avviso sonoro (nelle console testuali)
`v Tabulazione verticale (nelle console grafiche, come ISE)
$stars = ©{
AlphaCentauri = 4.3;
Barnard = 5.9;
Wolf359 = 7.8
}
$stars.GetEnumerator()
% { "$($_.Name)^t$($_.Value)" }
Il risultato è questo:
Barnard 5.9
Wolf359 7.S
AlphaCentauri 4.3
Le stringhe here
Le stringhe here rappresentano un costrutto per creare nuove
stringhe, alternativo a quello osservato in precedenza. A differenza
di quest’ultimo, ammette stringhe che contengono qualsiasi
sequenza di apici al loro interno e utilizza una coppia di caratteri
costituita da un apice (singolo o doppio) e il segno di a commerciale
(@) per definire l’inizio e il termine del testo da impiegare.
La sintassi delle stringhe here prevede che la sequenza dei due
caratteri di demarcazione sia invertita tra l’inizio e la fine della
stringa, secondo questo schema:
9 "
<Testo>
<Testo>
[...]
"@
L’ìndexer
Ogni stringa permette di recuperare agevolmente i caratteri da cui è
composta utilizzando il costrutto dell’indexer, alla stregua degli array.
Anche in questo caso gli indici degli elementi (ovvero dei caratteri)
partono da zero. Nei blocchi che seguono sono illustrati diversi
esempi d’uso di questo costrutto:
L’operatore -join
L’operatore -join permette di combinare tra loro con facilità una serie
di stringhe e restituirne una unica; nel caso siano forniti oggetti che
non sono stringhe il sistema si occupa, automaticamente, di
generarne una rappresentazione testuale. L’operatore utilizza
l’operando di destra per separare un elemento dal successivo
all’interno del valore ritornato.
La sintassi dell’operatore è:
Ecco come, per esempio, combinare più frammenti di testo tra loro
per generarne uno complessivo, dove l’elemento separatore è uno
spazio:
L’operatore -split
Questo operatore ha un obiettivo esattamente opposto a quello di -
join: frapponendolo tra una stringa e un delimitatore, infatti, la shell
PS C:\Users\ikmju> '1 ; 3,5 : 7/11|13' -split { $_ -gt '9' -or $_ -lt '0' }
1
3
5
7
11
13
La formattazione
In alternativa alla definizione di stringhe basate su doppi apici,
PowerShell permette di formattare un testo avvalendosi del supporto
per la formattazione incluso nel framework Microsoft .NET, che
consente di definire stringhe con punti di inserimento per altri valori e
di variare la rappresentazione testuale di questi ultimi. Il ruolo
centrale di questa tecnologia è assunto dal metodo statico Format() del
tipo System.String, cui è necessario fornire una stringa di formattazione
e i valori da includere nel risultato dell’elaborazione, secondo questo
schema:
L’operatore -f
Data la frequenza con cui il metodo Format() è utilizzato, tuttavia,
Microsoft ha aggiunto alla shell l’operatore -f, una scorciatoia
sintattica che permette di ottenere il medesimo risultato con un
codice più snello: facendo seguire a una stringa l’operatore -f, infatti,
questa viene passata a Format()come stringa di formattazione, mentre
tutti i valori che lo seguono sono passati come valori aggiuntivi.
La sintassi dell’operatore -f è:
PS C:\Users\ikmju> "{0} dista -{1} anni luce da qui." -f "Proxima Centauri", "4.23"
Proxima Centauri dista -4.23 anni luce da qui.
{<IndiceValore>:<Formato>}
Come anticipato, esistono stringhe di formato differenti per ciascun
tipo di oggetto presente nel framework Microsoft .NET: di
conseguenza non è possibile elencare tutte le possibili varianti e i
rispettivi obiettivi. Nel seguito del libro, in ogni caso, sono illustrate le
principali stringhe di formato per i tipi primitivi; per ulteriori
approfondimenti si rimanda al sito MSDN
(http://msdn.microsoft.com).
Metodi di rilievo
In questo paragrafo sono illustrati i metodi d’istanza della classe
System.String più utili per l’utente della shell.
PS C:\Users\ikmju> "aBC'-è%?".ToUpper()
ABC'-È%?
PS C:\Users\ikmju> "aBÇ-è%?"-ToLower()
abg-è%?
Metodo ToCharArray()
Ciascuna stringa memorizza internamente il proprio contenuto
all’interno di un array di caratteri, non modificabile dopo la creazione.
Il metodo ToCharArray() richiede al sistema di creare e restituire una
copia di questo array, così da poterlo esaminare o manipolare. Ecco
come, per esempio, ottenere un array costituito dai caratteri
successivi o uguali ad un particolare valore, impiegando questo
metodo e la capacità di PowerShell di filtrare gli elementi degli array:
PS C:\Users\ikmju> "Efran"-Length
5
PS C:\Users\ikmju> "Efran" -ToCharArray() - Length
5
Metodo SubstrìngO
Il metodo substring() consente di ottenere una particolare porzione di
una stringa ed è disponibile in due overload. Il primo ritorna la
porzione di stringa che inizia da una particolare posizione e accetta,
pertanto, un unico parametro:
<Stringa>- Substring(<Partenza>)
Nello script che segue, per esempio, sono estratte due stringhe a
partire da un’originale:
PS C:\Users\ikmju> "powershell".Substring(5)
shell
PS C:\Users\ikmju> "powershell".Substring(0, 5)
power
Metodo Remove()
Questo metodo agisce sulla stringa rimuovendone una porzione e
restituendo il risultato ottenuto. È disponibile in due overload, il primo
dei quali rimuove tutti i caratteri a partire da una particolare
posizione e accetta, pertanto, un unico parametro:
<Stringa>.Remove(<Partenza>)
<Stringa>.Remove(<Partenza>, <NumeroElementi>)
Nello script che segue, per esempio, il metodo Remove() elimina una
porzione centrale della stringa originale:
PS C:\Users\ikmju> "powershell".Remove(3,4)
powell
<Stringa>.IndexOf(<Ricerca>)
<Stringa>.IndexOf(<Ricerca>, <Posizione>)
I wildcard
I wildcard sono caratteri speciali cui la shell attribuisce un significato
particolare durante le operazioni di ricerca all’interno di un testo.
Questa tecnologia, di cui quasi tutte le shell esistenti sono provviste,
consente di costruire in modo molto semplice stringhe di ricerca in
grado di contenere segnaposto per uno o più caratteri, la cui entità e
lunghezza sono descritte dai segnaposto stessi.
Moltissimi cmdlet all’interno della shell consentono di sfruttare
questa tecnologia, specificando caratteri wildcard all’interno delle
stringhe fornite ai propri parametri, mentre gli operatori -like e -
notlike, come anticipato nel Capitolo 6, ne rendono possibile l’impiego
Wildcard *
Quando una stringa di ricerca contiene il carattere asterisco (*), la
shell vi abbina qualsiasi combinazione di testo, di qualsiasi
lunghezza (anche zero), all’interno della stringa da ricercare.
In questo script, per esempio, il wildcard * è utilizzato dal cmdlet Get-
ChildItem per recuperare tutti i file (e le cartelle) la cui estensione è
.txt:
Name
----
alias.txt
fakepid.txt
test.txt
[...]
Wildcard ?
Il simbolo del punto interrogativo (?) indica a PowerShell di abbinare,
durante l’operazione di confronto, un qualsiasi carattere.
Lo script che segue, quindi, richiede alla shell di ritornare tutti i file il
cui nome sia composto da test e un altro carattere, e un’estensione
qualsiasi:
Name
--
test1.txt
TestW.zip
Si noti come, in questo caso, utilizzando il wildcard ? la shell abbia
tralasciato il file test.txt, perché questo non dispone del carattere
necessario al match, infatti:
Wildcard []
Come il precedente, questo wildcard è un segnaposto per un unico
carattere e consente di indicare, uno di seguito all’altro, gli elementi
con cui può essere abbinato. Si può specificare qualsiasi carattere,
ma il trattino (-) ha un significato particolare: se è frapposto tra altri
due caratteri, infatti, la shell espande la sequenza con tutti gli
elementi compresi tra il carattere che lo precede e quello che lo
segue. Entrambi questi script, per esempio, sono soddisfatti dalla
stringa di ricerca utilizzata:
Name
----
conime
conime
Idle
LogonUI
[...]
Le espressioni regolari
Le espressioni regolari rappresentano una sorta di considerevole
evoluzione rispetto ai wildcard e permettono di definire stringhe di
ricerca utilizzando dei segnaposto ricchi di funzionalità e
particolarmente articolati. Questa tecnologia, il cui nome è spesso
abbreviato con l’acronimo regexp o regex, è disponibile all’interno
della piattaforma di scripting di PowerShell grazie agli operatori -match
(e il suo opposto -notmatch) e -replace, introdotti nel Capitolo 6. Alcuni
cmdlet, inoltre, sfruttano le espressioni regolari consentendone l’uso
attraverso i propri parametri; all’interno del framework Microsoft
.NET, infine, è possibile utilizzare la classe
System.Text.RegularExpressions.Regex per compiere grazie a questa
tecnologia ricerche e sostituzioni di testi. È possibile definire
un’espressione regolare utilizzando una stringa composta da
qualsiasi carattere: alcune sequenze, chiamate in gergo pattern,
variano tuttavia la modalità secondo cui la shell effettua i confronti.
Senza scendere nei dettagli del funzionamento delle espressioni
regolari - argomento al di fuori degli obiettivi di questo libro - nei
paragrafi che seguono sono descritti i pattern più utilizzati.
Pattern .
Quando un’espressione regolare contiene il simbolo punto (.) il
sistema lo abbina a qualsiasi carattere, tranne la sequenza di nuova
linea (`n). Questo pattern presenta notevoli somiglianze con il
wildcard ?, tant’è che nel suo impiego di base implica anch’esso la
presenza di un unico carattere nella stringa da ricercare, come
dimostrato da questi esempi:
Pattern []
Il pattern [] introduce una serie di caratteri ammessi nel match di un
particolare elemento della stringa da ricercare, in maniera simile a
quanto avviene per il wildcard omonimo. Anche in questo caso il
simbolo di trattino-meno permette di definire sequenze di caratteri
che il sistema espande automaticamente in fase di ricerca; a
differenza del wildcard [], tuttavia, questo pattern permette anche di
definire sequenze di caratteri che non devono essere abbinate alla
stringa da ricercare al fine di generare un’occorrenza. Per definire
questo tipo di sequenza è sufficiente farla precedere dal simbolo
dell’accento circonflesso (^).
Nello script che segue, per esempio, la prima espressione abbina il
pattern [] a qualsiasi carattere dell’alfabeto, mentre la seconda a
qualsiasi carattere che non sia una cifra; la terza espressione, infine,
sostituisce a tutte le cifre un carattere:
I punti dì ancoraggio
Quando il carattere del circonflesso (^) è al primo posto
dell’espressione regolare, al di fuori del pattern [], il sistema procede
con il match solo se la stringa da ricercare comincia con il resto della
sequenza dell’espressione. Se il simbolo del dollaro ($), viceversa, è
all’ultimo posto dell’espressione regolare, la stringa da ricercare
genera un match solo se termina con il resto della sequenza
dell’espressione.
Visto il possibile impiego, entrambi i caratteri in questa
configurazione sono noti come punti di ancoraggio.
Manipolando una delle espressioni proposte in precedenza, d’altra
parte, è facile notare come la mancanza di questi costrutti possa
determinare risultati che ci si potrebbe non aspettare:
I quantificatori
Gli elementi di questa famiglia di pattern permettono di indicare il
numero di volte che una particolare sequenza deve essere ripetuta
all’interno della stringa da ricercare affinché vi sia un match.
Il quantificatore introdotto dal simbolo del punto interrogativo (?)
rende la sequenza che lo precede opzionale.
In questo script, per esempio, si ottengono sia i processi di explorer
sia quelli di iexplore:
Name
----
Test.txt
Test9x.txt
Test9x9x.txt
Test9x9x9x.txt
Name
----
Test9x.txt
Test9x9x.txt
Test9x9x9x.txt
Classi di carattere
Le classi di carattere sono sequenze speciali di simboli che il
sistema riconosce automaticamente ed espande in un pattern [] ben
definito. Poiché, generalmente, le classi di carattere sono individuate
da una coppia di simboli, questo sistema consente di risparmiare
tempo nella creazione dell’espressione regolare che, di
conseguenza, risulta anche più leggibile.
Tabella 13.1 - Le principali classi di carattere.
Sequenza Classe risultante
\w Carattere alfanumerico
\W Carattere non alfanumerico
\d Cifra
\D Non cifra
\s Spazio bianco
\s Non spazio bianco
Le alternanze
Alla stregua di quanto avviene all’interno del pattern [] per i caratteri,
le espressioni regolari consentono di definire intere sequenze
alternative per la ricerca delle occorrenze. Le alternanze - questo è il
nome del costrutto - sono definite grazie a una coppia di parentesi
tonde che contengono le espressioni regolari desiderate, separate
dal carattere pipe ( |); il sistema considera la stringa di ricerca un
match quando verifica una qualsiasi di queste sequenze alternative.
La sintassi per la definizione delle alternanze è dunque:
;<Alternatival>|<Alternativa2>...)
Name
----
Vacanze.xlsx
Visio for Enterprise Architectes e VS.NET.docx
VPS Hosting.xls
Le sequenze di escape
Il simbolo backslash permette di introdurre all’interno delle
espressioni regolari anche sequenze standard di caratteri che il
sistema espande, in maniera pressoché identica a quanto avviene
con l’apice inverso per le sequenze di escape della shell.
PS C:\Users\ikmju> β"
>> 1234567
>> abede
>> "β -match "^Xdi7}\n\w*$"
>>
True
I gruppi
Gli insiemi di caratteri racchiusi tra parentesi tonde, nel gergo delle
espressioni regolari, prendono il nome di gruppi. Quando si verifica
un match tra una stringa da ricercare e un’espressione regolare che
contiene dei gruppi, il sistema estrae dalla prima il testo di
pertinenza di ciascun gruppo della seconda, assegnando a ognuno
un numero in base alla posizione del gruppo all’interno
dell’espressione. Ogni coppia costituita dal numero del gruppo e dal
relativo testo di pertinenza, infine, è aggiunta all’array associativo
$matches, consultabile e manipolabile dall’utente; il gruppo numero
PS C:\Users\ikmju> $matches
Name Value
---- -----
2 www.powershell.it/Forum.aspx
1 http
0 http://www.powershell.it/Forum.aspx
Come anticipato, infine, è possibile operare su $matches per recuperare
i dati del gruppo di interesse:
PS C:\Users\ikmju> $matches[l]
http
PS C:\Users\ikmju> $matches[2]
www.powershell.it/Forum.aspx
Select-String
In aggiunta agli operatori dedicati al trattamento delle espressioni
regolari, la shell mette a disposizione degli utenti un cmdlet chiamato
Select-String, in grado di impiegare questa tecnologia per ricercare del
powershell - it
Tipi numerici
Il framework Microsoft .NET supporta nativamente la
rappresentazione e la manipolazione dei numeri attraverso ben 11
tipi differenti, a seconda del fatto che si desideri operare con valori
interi o decimali e della scala da utilizzare. Nonostante l’ampia
offerta, PowerShell è sempre in grado di determinare per ogni
occasione quale sia il tipo più adatto per la memorizzazione dei
valori, così che non sia necessario esplicitarne i tipi: conoscerne le
peculiarità, tuttavia, può facilitare la stesura e la comprensione del
codice e rendere possibili scenari di maggiore complessità.
Byte e SByte
Il tipo System.Byte occupa 8 bit di memoria e permette di gestire valori
interi naturali nell’intervallo che va da 0 a 255, per un totale di 28
valori possibili; il tipo System. SByte (acronimo di Signed Byte) è identico
al precedente ma riserva un bit per memorizzare l’informazione del
segno, ammettendo valori interi da un minimo di -128 ad un
massimo di 127. All’interno della shell la frequenza d’uso per
entrambi i tipi è generalmente limitata al supporto per l’elaborazione
di file binari e all’impiego di strutture definite all’interno del framework
stesso.
Int16 e Ulnt16
Il tipo System.Int16 (chiamato short, nella maggior parte dei linguaggi di
sviluppo di derivazione C) gestisce tramite 16 bit i valori interi che
vanno da -32768 a 32767, per un totale di 216 valori possibili;
System.uInt16 (acronimo di Unsigned Integer) è la versione senza
Int32 e Ulnt32
Salendo la scala dei tipi numerici si trova System.Int32, che consente di
gestire valori interi a 32 bit con un intervallo che spazia da
-2147483648 a 2147483647, per un totale di 232 possibilità. La
versione priva di segno è chiamata System.Int32 e si distingue dalla
precedente per una scala che ammette valori da 0 a 4294967295. Il
tipo System.Int32 è utilizzato automaticamente dalla shell quando il
risultato di un’espressione è un intero che rientra nell’intervallo
ammesso da questo tipo, come dimostra questo script:
PS C:\Users\ikmju> (9).GetType().Name
Int32
PS C:\Users\ikmju> (9783).GetType().Name
Int32
PS C:\Users\ikmju> (-299721).GetType().Name
Int32
Single
Il tipo System.Single gestisce approssimazioni di numeri reali,
utilizzando uno standard definito dall’IEEE (Institute of Electrical and
Electronic Engineers). I valori ammessi spaziano da -3,402823·1038
a 3,402823·1038 con una precisione di sette cifre decimali, in 32 bit
di spazio occupato. Dei tipi numerici non interi, questo è quello
utilizzato meno frequentemente all’interno della shell.
Double
Il tipo System-Double è molto simile al precedente, ma gestisce
approssimazioni di numeri reali il cui valore può spaziare da
-1,79769313486232·10308 a 1,79769313486232·10308 in 64 bit di
spazio occupato, con una precisione di 15-16 cifre decimali.
Quando il risultato di un’espressione è un numero non intero, la shell
utilizza in maniera trasparente System.Double per immagazzinarne il
valore, come dimostra questo breve script:
PS C:\Users\ikmju> (7.83).GetType().Name
Double
Decimal
Questo tipo permette di definire numeri decimali con una precisione
molto più elevata dei due precedenti, ma con una scala di valori
minore. System.Decimal occupa 128 bit di memoria e ammette valori a
partire da -7,9·1028 fino ad arrivare a 7,9·1028, con una precisione di
28-29 cifre decimali. Si tratta senz’altro del tipo da utilizzare quando
si desidera portare a termine operazioni matematiche con numeri
decimali ottenendo il massimo della precisione possibile all’interno di
PowerShell.
[decimal]123.456
PS C:\Users\ikmju> [byte]999
Impossibile convertire il valore "999" nel tipo "System.Byte". Errore:
"Valore troppo grande o troppo piccolo per un Unsigned Byte."
In riga:1 car:7
+ [byte] << 999
+ Categorylnfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorld : RuntimeException
Operazioni fondamentali
Tutti i tipi numerici possono essere manipolati grazie agli operatori
aritmetici, introdotti nel libro a partire dal Capitolo 3. Per ogni
espressione in cui sono coinvolti valori numerici, la shell determina
automaticamente quale tipo utilizzare per ospitarne il risultato, in
maniera tale da non richiederne all’utente un’esplicitazione: nel caso
in cui un’espressione contenga termini numerici di tipo differente,
inoltre, il sistema è in grado di convertirne automaticamente i valori
prima di procedere a eventuali operazioni.
<Espressione> + <Espressione>
<Espressione> - <Espressione>
<Espressione> * <Espressione>
<Espressione> / <Espressione>
<Espressione> % <Espressione>
$x = $x + $y
$x += $y
$x++
++$x
PS C:\Users\ikmju> $x = 2
PS C:\Users\ikmju> 7 * ($x++)
14
PS C:\Users\ikmju> $x = 2
PS C:\Users\ikmju> 7 * (++$x)
21
Le notazioni numeriche
PowerShell riconosce automaticamente le notazioni numeriche più
utilizzate. La notazione decimale è quella standard, impiegata fino
a questo punto del libro per definire i numeri e usata di default da
PowerShell per visualizzarli a video. Il simbolo di separazione tra la
parte intera e quella decimale è il punto (.), a prescindere dalle
impostazioni internazionali della macchina su cui si sta operando.
Poiché, come riportato in precedenza, il tipo utilizzato di default per i
numeri non interi è Double, nel caso si desideri forzare il tipo Decimal è
possibile far seguire all’indicazione del valore la lettera d. Lo script
che segue dimostra le due alternative:
PS C:\Users\ikmju> (123.45).GetType().Name
Double
PS C:\Users\ikmju> (123.45d).GetType().Name
Decimal
PS C:\Users\ikmju> 2.997925e+S
29979250C
[Convert]::ToInt32(<Stringa>, <Base>)
[convert]::ToInt64(<Stringa>, <Base>)
[... ]
PS C:\Users\ikmju> [Convert]::ToInt32("1001010001", 2)
593
[Convert]::ToString(<Numero>, <Base>)
L’operatore -band
Dati due operandi interi (o convertibili in intero), l’operatore -band
(acronimo di binary and) ritorna un terzo valore i cui bit sono uguali a
uno dove i rispettivi bit nei due operandi sono pari a tale valore.
Questo operatore, quindi, può essere considerato a tutti gli effetti
l’equivalente binario di -and.
Nello script che segue, per esempio, l’operatore -band è utilizzato per
calcolare l’and binario tra due valori:
PS C:\Users\ikmju> 123 -band 456
72
L’operatore -bor
L’operatore -bor (acronimo di binary or) ritorna un intero i cui bit sono
uguali a uno quando almeno uno dei rispettivi bit nei due operandi è
pari a tale valore. Questo operatore, quindi, può essere considerato
a tutti gli effetti l’equivalente binario di -or.
Nello script che segue, per esempio, l’operatore -bor è utilizzato per
calcolare l’or binario tra due valori:
L’operatore -bxor
Quando è frapposto tra due valori numerici, l’operatore -bxor
(acronimo di binary exclusive or) ne ritorna un terzo i cui bit sono
uguali a uno quando solo uno dei rispettivi bit nei due operandi è pari
a tale valore. Questo operatore, quindi, può essere considerato a
tutti gli effetti l’equivalente binario di -xor.
L’operatore -bnot
Mentre tutti gli operatori analizzati in precedenza utilizzano due
espressioni distinte per generarne una terza, l’operatore -
bnot(acronimo di binary not) ammette come unico operando quello di
Il tipo System.Math
Il tipo System.Math fornisce un ampio supporto per l’impiego delle
funzioni matematiche più comuni, incluse quelle esponenziali,
logaritmiche e trigonometriche, ed espone una serie di proprietà e
metodi statici con cui è possibile interagire. In questo paragrafo sono
discusse le funzionalità più comuni tra gli utenti di PowerShell.
Costanti matematiche
Le proprietà statiche PI ed E consentono di ottenere, rispettivamente,
il valore di π e quello di e (la costante di Nepero). Entrambi i valori
sono ritornati sotto forma di Double.
Nello script che segue, per esempio, sono recuperati e visualizzati
entrambi:
PS C:\Users\ikmju> [Math]::PI
3.14159265358979
PS C:\Users\ikmju> [Math]::E
2.71828182845905
Metodo Pow()
Il metodo statico POW() eleva un numero alla potenza specificata,
ritornando il risultato. La sintassi è questa:
[Math]::Pow(<Base>, <Esponente>]
Metodo Sqrt()
Di rimando, il metodo statico sqrto - acronimo di square root - calcola
la radice quadrata del valore specificato, in base a questa semplice
sintassi:
[Math]::Sqrt(<Numero>]
Metodo Truncate()
Questo metodo statico accetta un unico valore numerico come
parametro e ne ritorna la sola parte intera. La sintassi - banale - è
questa:
[Math]::Truncate(<Numero>]
PS C:\Users\ikmju> [Math]::Truncate(567.89)
567
PS C:\Users\ikmju> [Math]::Round(1.5)
2
PS C:\Users\ikmju> [Math]::Round(2.5)
2
PS C:\Users\ikmju> [Math]::Round(3.5)
4
[Math]::Round(<Numero>)
[Math]::Round(<Numero>, <Arrotondamento>)
Il tipo System.Random
Nonostante non faccia letteralmente parte dei tipi
numerici,System.Random espone una funzionalità molto utile in tutti quei
contesti dove si desidera ottenere rapidamente un valore numerico
casuale.
Metodo Next()
Tutto il funzionamento di questo tipo ruota attorno a questo metodo
di istanza, che dispone di tre overload distinti. Il primo non accetta
parametri e restituisce un valore Int32 positivo casuale; il secondo
accetta un unico intero e ritorna un valore Int32 positivo casuale
minore del numero specificato. Il terzo overload, infine, permette di
specificare un valore minimo e uno massimo e ritorna un Int32
casuale all’interno dell’intervallo descritto.
Le sintassi per questi overload sono:
<Random>.Next()
<Random>.Next(<Massimo>)
<Random>.Next(<Minimo>, <Massimo>)
Get-Date
Questo cmdlet è il più importante dei comandi dedicati alla gestione
delle date e ricopre una duplice funzione. Richiamato senza
specificare alcun parametro, infatti, Get-Date ritorna la data e l’ora
correnti, alla stregua di quanto è possibile fare con il comando date,
presente sia nei sistemi *nix sia in CMD e COMMAND.COM. Gli sviluppatori di
PowerShell, d’altra parte, hanno aggiunto date agli alias predefiniti di
, in maniera tale da rendere più facile l’adozione del nuovo
Get-Date
PS C:\Users\ikmju> Get-Date
PS C:\Users\ikmju> (Get-Date).Month
3
16.32.48
Formattare le date
Il cmdlet consente, inoltre, di ritornare direttamente delle stringhe
come risultato della formattazione personalizzata della data e
dell’ora di riferimento: agendo sul parametro -Format, infatti, si può
specificare una stringa di formattazione che indica come
rappresentare l’oggetto.
La stringa di formattazione yyyy-MM-dd, per esempio, induce Get-Date a
produrre una stringa composta, rispettivamente, dall’anno espresso
in quattro cifre, dal mese espresso in due e dal giorno espresso,
anch’esso, in due cifre:
suddetto:
Manipolare le date
Gli oggetti di tipo System.DateTime sono dotati di alcuni utili metodi che
consentono di effettuare operazioni sulle date; poiché questi oggetti,
una volta creati, non si possono modificare, i metodi in questione
non alterano il valore contenuto dall’istanza cui sono sottesi, ma ne
creano una nuova, provvista del valore desiderato, e la ritornano al
chiamante.
Alcuni metodi d’istanza di questa classe sono utilizzati per
aggiungere uno specifico intervallo temporale a una data: il metodo
AddYears() ritorna una data che differisce da quella corrente per il
<DateTime>.AddYears(<Anni>)
<DateTime>-AddMonths(<Mesi>)
<DateTime>-AddDays(<Giorni>)
<DateTime>-AddHours(<Ore>)
<DateTime>-AddMinutes(<Minuti>)
<DateTime>-Addseconds(<Secondi>)
Nello script che segue, per esempio, si sottraggono 3 anni dalla data
corrente:
PS C:\Users\ikmju> (date)-AddYears(-3)
Monday, March 05, 2007 4:32:22 PM
[DateTime]::DaysInMonth(<Anno>, <Mese>)
PS C:\Users\ikmju> [DateTime]::DaysInMonth(2010, 2)
28
PS C:\Users\ikmju> [DateTime]::DaysInMonth(2010, 2)
29
PS C:\Users\ikmju> [DateTime]::DaysInMonth(2010, 2)
28
[DateTime]::IsLeapYear(<Anno>)
PS C:\Users\ikmju> [DateTime]::IsLeapYear(2010)
False
PS C:\Users\ikmju> [DateTime]::IsLeapYear(2012)
True
PS C:\Users\ikmju> [DateTime]::IsLeapYear(2100)
False
<DateTime>.IsDaylightSavingTime()
<TimeZone>.GetDaylightChanges(<Anno>)
PS C:\Users\ikmju> [TimeZone]::CurrentTimeZone.GetDaylightChanges(2010)
PS C:\Users\ikmju> [TimeZone]::CurrentTimeZone
StandardName DaylightName
------------ ------------
ora solare Europa occidentale ora legale Europa occidentale
secondi:
Days : 0
Hours : 0
Minutes : 9
Seconds : 7
[...]
Days : 0
Hours : 0
Minutes : 10
Seconds : 30
[...]
<DateTime>-Add(<TimeSpan>)
Set-Date
Il cmdlet Set-Date è in grado di reimpostare le informazioni sulla data e
l’ora di sistema. Fornendo un oggetto System.DateTime al parametro -Date
il comando utilizza direttamente questo valore, mentre impiegando il
parametro -Adjust e un intervallo temporale la shell aggiunge
quest’ultimo al valore della data corrente, variandola. Le sintassi di
chiamata diSet-Date, dunque, sono rappresentate da questo schema:
Le funzioni
Una funzione consiste in una o più istruzioni raggruppate all’interno
di un unico blocco, cui è stato assegnato un nome; in seguito alla
definizione, questo nome può essere impiegato come un nuovo
comando e la sua digitazione porta a termine le istruzioni contenute
nel rispettivo blocco. Generalmente, quindi, si tende a creare
funzioni laddove sia conveniente riutilizzare particolari istruzioni
all’interno dei propri script.
La sintassi di definizione delle funzioni è molto simile a quella
presente in altri linguaggi di sviluppo: le istruzioni che ne formano il
corpo sono raggruppate utilizzando una coppia di parentesi graffe,
mentre l’intero blocco deve essere preceduto dalla keyword function
seguita dal nome desiderato. Ecco lo schema sintattico:
function <Nome>
{
<Istruzione1>
<Istruzione2>
<Istruzione3>
[...]
}
valore di verità logica a seconda del fatto che l’utente che esegue lo
script faccia parte o meno del gruppo Administrators della macchina
locale. Nonostante il codice contenga alcuni riferimenti a due utili
classi del framework Microsoft .NET, il seguito di questo paragrafo si
limita a illustrare i concetti legati direttamente alle funzioni,
tralasciando invece i dettagli legati al codice nel corpo di queste
ultime.
function Is-Administrator
{
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal $identity
$principal.IsInRole('BUILTIN\Administrators')
}
PS C:\Users\ikmju> Is-Administrator
True
Gli oggetti emessi nella pipeline dalla funzione sono disponibili per
ulteriori elaborazioni in un blocco di pipeline successivo; le funzioni,
quindi, non sono limitate alla restituzione di un unico oggetto al
chiamante – paradigma molto diffuso in quasi tutti i linguaggi di
sviluppo – ma partecipano attivamente all’elaborazione della pipeline
e sono in grado di erogare una sequenza di valori.
Questa funzione, per esempio, ritorna la sequenza dei moduli
principali per tutti i processi della macchina locale:
function Get-MainModule
{
Get-Process
Select-Object -Expand MainModule
}
FileName
--------
C:\Windows\system32\conime.exe
C:\Windows\system32\Dwm.exe
C:\Windows\Explorer.EXE
[...]
function Test-Return
{
1
2
return 3
4
5
}
E genera un output simile a questo:
Lo scope
Le variabili definite all’interno di una funzione sono soggette alla
stessa protezione prevista per gli script esterni, analizzata nel
Capitolo 4. L’esecuzione di una funzione, infatti, porta alla creazione
di un nuovo scope, che consente di proteggere e incapsulare
funzionalità indipendenti dall’esterno del corpo, facilitando ancora
una volta, quindi, il riutilizzo del codice.
Grazie a una sintassi analoga a quella impiegata dalle variabili,
inoltre, è possibile indicare a PowerShell in quale scope definire
un’intera funzione, con benefici simili a quanto descritto in
precedenza. È sufficiente far precedere al nome della funzione lo
scope desiderato, come illustrato in questo schema:
function [<Scope:>]<Nome>
{
[...]
}
function global:Do-Testl
{
"Test 1"
}
function Do-Test2
{
"Test 2"
}
PS C:\Users\ikmju> .\GlobalScopeFunction.PS1
PS C:\Users\ikmju> Do-Test2
Termine 'Do-Test2' non riconosciuto come nome di cmdlet, funzione, programma
eseguibile o file script- Controllare l'ortografìa del nome o verificare che il
percorso sia incluso e corretto, quindi riprovare.
In riga:l car : 9
+ Do-Test2 <<<<
+ Categorylnfo : ObjectNotFound: (Do-Test2:String) [],
CommandNotFoundException
+ FullyQualifìedErrorld : CommanclNotFoundException
NO In questa circostanzaGet-command ritorna sia funzioni sia filtri, trattati nel seguito del
TA capitolo.
I parametri
In modo simile a quanto avviene per i cmdlet e per gli script esterni,
anche le funzioni supportano il passaggio di valori tramite parametri,
che possono essere definiti utilizzando due sintassi diverse a
seconda della preferenza dell’utente. La prima impiega la keyword
param, alla stregua di quanto già visto per gli script esterni nel Capitolo
4; in questo caso il blocco param deve essere il primo elemento del
corpo della funzione, come in questo esempio:
function Is-Even
{
param ($value)
($value % 2) -eq 0
}
function Is-Even($value)
{
($value % 2) -eq 0
}
function <Nome>
{
param (<Parametro1> [, <Parametro2>] [, ...])
[...]
}
PS C:\Users\ikmju> Is-Even 3
False
PS C:\Users\ikmju> Is-Even 100
True
A meno che non si adoperi una delle tecniche esposte nel seguito di
questo capitolo, ciascun parametro è opzionale e, nel caso in cui
non sia fornito alcun valore, la shell lo considera pari a $null. La
funzione precedente, per esempio, ritorna $true quando viene
richiamata senza specificare parametri perché la variabile $value
assume di conseguenza il valore $null, che convertito a intero è pari
a 0:
PS C:\Users\ikmju> Is-Even
True
Alla stregua di quanto avviene per gli script esterni, inoltre, anche i
parametri delle funzioni possono essere fortemente tipizzati: è
sufficiente, anche in questo caso, far precedere al nome del
parametro il tipo atteso, tra parentesi quadre. La funzione
dell’esempio, quindi, può essere riscritta così, in maniera tale da
rendere esplicito il tipo atteso:
function Is-Even([Int32]$value)
{
($value % 2) -eq 0
}
function Get-TimeToApocalypse([switch]$asMinutes)
{
$timeToApocalypse = ([datetime]'2012-12-21') - (date)
if ($asMinutes)
{
$timeToApocalypse-TotalMinutes
}
else
{
$timeToApocalypse
}
}
Days : 1015
Hours : 7
[...]
[<Tipo>]<Parametrol> [ = <ValorePredefinito>]
$principal-IsInRole($role)
}
PS C:\Users\ikmju> Is-InRole
True
PS C:\Users\ikmju> Do-Test 3
$x -> 3
$y ->
PS C:\Users\ikmju> Do-Test -y 5
$x ->
$y -> 5
PS C:\Users\ikmju> Do-Test 3 5
$x -> 3
$y -> 5
PS C:\Users\ikmju> Do-Test -y 3 5
$x -> 5
$y -> 3
PS C:\Users\ikmju> Do-Test 2 3 5
$x -> 2
$y -> 3
Posizionale: 5
PS C:\Users\ikmju> Do-Test 2 3 -x 5 7
$x -> 5
$y -> 2
Posizionale: 3
Posizionale: 7
[Parameter(Mandatory=$true)]
<Parametro>
function Is-Even(
[Parameter(Mandatory=$true)]
[Int32]
$value
)
{
($value % 2) -eq 0
}
PS C:\Users\ikmju> Is-Even
Cmdlet Is-Even nella posizione 1 della pipeline dei comandi
Specificare i valori per i seguenti parametri:
value:
[Parameter(Mandatory=$true)]
[AllowNull()]
<Parametro>
function Is-Even(
[Parameter(Mandatory=$true)]
[AllowNull()]
[Int32]
$value
)
{
($value % 2) -eq 0
}
[Parameter(Mandatory=$true)]
[AllowEmptyString()]
<Parametro>
function Do-Test(
[Parameter(Mandatory=$true)]
[ValidateCount(3, 5)]
[Int32[]]
$value
)
{
[...]
}
PS C:\Users\ikmju> Do-Test 1, 2
Do-Test : Impossibile convalidare l'argomento sul parametro 'value'. Il numero
di argomenti forniti (2) è inferiore al numero minimo di argomenti consentiti
(3). Specificare più di 3 argomenti ed eseguire di nuovo il comando.
[...]
function Do-Test(
[ValidateSet('Staging', 'Production')]
$value
)
{
[...]
}
Due costrutti sono dedicati alla gestione dei parametri di tipo stringa.
Il primo serve per convalidare la lunghezza del testo fornito:
[ValidateLength(<Minimo>, <Massimo>)]
<Parametro>
function Do-Test(
[ValidateLength(2, 7)]
$value
)
{
[...]
}
[ValidatePattern('<EspressioneRegolare>')]
<Parametro>
function Do-Test(
[ValidatePattern('^\d{11}$')]
$value
)
{
[...]
}
[ValidateRange(<Minimo>, <Massimo>)]
<Parametro>
function Do-Test(
[ValidateRange(10, 100)]
$value
)
{
[...]
}
PS C:\Users\ikmju> Do-Test 5
Do-Test : Impossibile convalidare l'argomento sul parametro 'value'.
L'argomento 5 è minore dell'intervallo minimo consentito di (10). Fornire un
argomento maggiore di 10 ed eseguire di nuovo il comando.
[...]
[ValidateScript(<Script>)]
<Parametro>
function Do-Test(
[ValidateScript({ ($_ % 2) -eq 0 })]
$value
)
{
[...]
}
PS C:\Users\ikmju> Do-Test 3
Do-Test : Impossibile convalidare l'argomento sul parametro 'value'. Lo
script di convalida " ($_ % 2) -eq 0 " per l'argomento con valore "3" non ha
restituito true. Determinare il motivo per cui lo script di convalida non è
riuscito ed eseguire di nuovo il comando.
[...]
automatica $input.
Per interagire con gli oggetti provenienti dalla pipeline, dunque, è
sufficiente iterare la collezione $input e portare a termine l’attività
desiderata per ciascun elemento trovato. Supponendo di voler
creare una funzione che ritorni la radice quadrata dei valori presenti
nella pipeline, per esempio, è possibile servirsi di uno script simile a
questo:
function Get-SquareRoot()
{
foreach ($x in $input)
{
[Math]::Sqrt($x)
}
}
function <Nome>
{
begin
{
<Comandi>
}
process
{
<Comandi>
}
end
{
<Comandi>
}
}
function Get-SquareRoot()
{
process
{
[Math]::Sqrt($_)
}
}
function Sum-Object
{
begin
{
$sum = $null
}
process
{
if ($sum -eq $null)
{
$sum = $_
}
else
{
$sum += $_
}
}
end
{
$sum
}
}
I filtri
Alla luce di quanto illustrato in precedenza, dunque, definire una
funzione utilizzando la sintassi di base è equivalente a impiegare la
sintassi che permette l’interazione con la pipeline, inserendo il
codice desiderato nel blocco end.
I filtri sono funzioni che si distinguono dalle precedenti per il solo
fatto che i comandi contenuti nel loro corpo sono eseguiti, quando si
impiega la sintassi di base, per ogni elemento emesso
precedentemente nella pipeline. Si tratta, dunque, di un
comportamento analogo all’inserire il codice desiderato in un blocco
process di una normale funzione. Per tale ragione, i filtri costituiscono
filter <Nome>
{
<Istruzionel>
<Istruzione2>
<Istruzione3>
[...]
}
filter <Nome>
{
begin
{
<Comandi>
}
process
{
<Comandi>
}
end
{
<Comandi>
}
}
filter Get-SquareRoot()
{
[Math]::Sqrt($_)
}
L’architettura
All’interno di PowerShell i drive non sono limitati a una sola lettera,
ma possono essere stringhe di qualsiasi lunghezza, anche se di
solito non superano i 4-5 caratteri. In ogni percorso assoluto il drive
è semplicemente la porzione di stringa che va dal primo carattere al
simbolo dei due punti (:), che funge da separatore. Genericamente,
dunque, la sintassi d’uso dei drive è simile a quella di CMD e COM-
MAND.COM:
<Drive>:<Percorso>
PS C:\Users\ikmju> Get-PSProvider
Name PSSnapIn
---- --------
WSMan Microsoft.WSMan.Management
Alias Microsoft.PowerShell.Core
Environment Microsoft.PowerShell.Core
FileSystem Microsoft.PowerShell.Core
Function Microsoft.PowerShell.Core
Registry Microsoft.PowerShell.Core
Variable Microsoft.PowerShell.Core
Certificate Microsoft.PowerShell.Security
I provider
Prima di analizzare i comandi dedicati alla gestione dei drive e dei
file conviene esaminare le possibilità offerte dai differenti tipi di
provider supportati nativamente da PowerShell.
II provider FileSystem
Il provider FileSystem gestisce le unità, le cartelle e i file del file system
e, all’avvio di PowerShell, crea un drive per ogni unità rilevata nella
macchina (incluse le unità di rete). Nello script che segue, per
esempio, si può notare come questo provider abbia creato un drive
per ogni unità disponibile nel file system di una macchina di test,
incluse l’unità del disco floppy (A:), quella del lettore DVD (D:) e
un’unità di rete (z:):
Il provider Registry
Il provider Registry permette di gestire il registry di Windows,
recuperandone e impostandone la struttura, le chiavi e i valori.
Nonostante questo provider supporti l’accesso a tutti gli hive del
registry, all’avvio di PowerShell si limita alla creazione di un drive per
HKEY_CURRENT_USER e uno per HKEY_LOCAL_MACHINE, rispettivamente HKCU: e HKLM:.
Il provider Variable
Il provider Variable consente di gestire le variabili presenti nella
sessione corrente. A ogni variabile, infatti, corrisponde un elemento
presente all’interno del drive Variable:.
Il provider Alias
In modo simile al precedente, questo provider consente il recupero e
la modifica degli alias presenti nella sessione corrente. A ogni alias,
quindi, corrisponde un elemento presente all’interno del drive Alias:.
Il provider Function
Analogamente ai due provider precedenti, il provider Function gestisce
tutte le funzioni presenti nella sessione corrente e a ognuna fa
corrispondere un elemento del drive omonimo.
Il provider Environment
L’utilissimo provider Environment consente di accedere rapidamente alle
variabili di ambiente per la sessione corrente, rendendole disponibili
attraverso gli elementi del drive Env:.
Il provider Certificate
Il provider Certificate gestisce i certificati X.509 installati nella
macchina locale e permette il recupero immediato di tutti i dettagli,
come gli enti emettitori e le date di validità. Il drive cert:, governato
da questo provider, espone una struttura gerarchica simile a quella
esibita nella console MMC (Microsoft Management Console) dallo
snap-in Certificati.
Il provider WSMan
Il provider WSMan è presente a partire dalla versione 2.0 di
PowerShell e consente di recuperare e impostare la configurazione
di WinRM (Capitolo 1). Ogni singola impostazione di questo sistema è
gestita attraverso gli elementi del drive WSMan:.
Get-Location
Nel suo impiego più semplice, questo cmdlet ritorna un oggetto di
tipo System.Mana-gement.Automation.PathInfo che descrive il percorso di lavoro
corrente. Questa classe contiene i riferimenti al provider, al drive e
alla posizione di riferimento all’interno dello stesso, così come ci si
potrebbe aspettare.
Nello script che segue, per esempio, si utilizza Get-Location dapprima
per rappresentare a video la posizione corrente, poi per recuperare il
provider associato:
PS C:\Users\ikmju> Get-Location
Path
----
C:\Users\ikmju
PS C:\Users\ikmju> (Get-Location).Provider
Set-Location
Il comando Set-Location agisce in maniera speculare rispetto a Get-
Location e imposta il percorso di lavoro corrente. Mediante il
parametro posizionale -Path di questo cmdlet, infatti, è possibile
specificare una stringa che identifica univocamente un percorso
all’interno di uno dei drive supportati dalla shell.
Nello script che segue, per esempio, si utilizza questo cmdlet per
impostare il percorso di lavoro corrente su di una particolare cartella
del drive D:
PS C:\Users\ikmju> Set-Location ..
PS C:\Users>
PS C:\Users> Set-Location Function:
PS Function:\> Set-Location ..
PS Function:\>
Get-ChildItem
Il cmdlet Get-ChildItem ritorna la sequenza di elementi contenuti in un
percorso specifico, gestito da un provider caricato nella sessione. È
possibile indicare a questo comando il percorso desiderato
impiegando il parametro posizionale -path; nel caso in cui tale valore
sia omesso, Get-chiiditem recupera gli elementi figlio presenti nel
percorso di lavoro corrente. Questo cmdlet ritorna tipi di oggetto
diversi a seconda del provider utilizzato e dell’elemento ritornato.
• il provider FileSystem ritorna oggetti di tipo Fileinfo per indicare un
elemento di tipo file e oggetti di tipo Directoryinfo per le cartelle.
Entrambe le classi risiedono nel namespace System.IO;
• il provider Registry ritorna oggetti di tipo Microsoft.Win32.RegistryKey;
• la maggior parte degli oggetti ritornati dal provider variable è
un’istanza della classe System.Management.Automation.PSVariable. Alcuni
elementi, inoltre, sono ritornati utilizzando tipi specifici differenti,
appartenenti al namespace System.Management.Automation;
• il provider Alias ritorna elementi di tipo System-
Management.Automation.AliasInfo;
valore;
• il provider Certificate ritorna elementi di tipo X509certificate2 per i
certificati e X509store per i contenitori, entrambi appartenenti al
namespace System. Security.Cryptography.X509Certificates;
• il provider WSMan ritorna oggetti di tipo diverso, in base
all’elemento di confi gurazione corrispondente. Tutte le classi,
tuttavia, risiedono nel namespace Microsoft.WSMan.Management.
In questo script, per esempio, il cmdlet Get-ChildItem è utilizzato per
recuperare gli elementi figlio di un percorso di un drive del file
system:
Directory: C:\Windows
Hive: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control
Name Value
---- -----
ALLUSERSPROFILE C:\ProgramData
APPDATA C:\Users\ikmju\AppData\Roaming
CLIENTNAME ALKAID
CommonProgramFiles C:\Program Files\Common Files
COMPUTERNAME WIN-TT6M1MHLDN1
Name : SmartCardRoot
Name : AuthRoot
Subject : CN=GlobalSign Root CA, OU=Root CA, O=GlobalSign nv-sa, C=BE
Issuer : CN=GlobalSign Root CA, OU=Root CA, O=GlobalSign nv-sa, C=BE
Thumbprint : B1BC968BD4F49D622AA89A81F2150152A41D829C
FriendlyName : GlobalSign
NotBefore : 01/09/1998 14.00.00
NotAfter : 28/01/2028 13.00.00
Extensions : {System.Security.Cryptography.Oid, System.Security.
Cryptography.Oid, System.Security.Cryptography.Oid}
Get-Item
Questo cmdlet ritorna la sequenza di item specificati utilizzando il
parametro -path e, tramite lo switch -Force, consente di recuperare
anche gli elementi nascosti, come i file di sistema. La sintassi è
molto semplice:
L’esempio che segue illustra come sia possibile utilizzare Get-item per
recuperare una particolare cartella del file system:
Directory:
New-Item
Questo cmdlet crea un nuovo elemento all’interno di un percorso
gestito da un provider di PowerShell. Accetta il parametro
posizionale -path, tramite il quale è possibile specificare il percorso e
il nome di destinazione dell’elemento e, nel caso in cui il provider
supporti più di un tipo di elemento, accetta il parametro -ItemType, con
cui si può esplicitare una stringa specifica del provider di riferimento.
Con il parametro -Value, inoltre, è possibile associare un oggetto che
rappresenta il valore dell’elemento da creare, che eventualmente
può anche essere recuperato in automatico dalla pipeline.
Lo switch -Force, infine, permette di creare un elemento anche
quando questo ne sovrascrive uno esistente, marcato per l’accesso
in sola lettura: in tal caso, infatti, il comportamento predefinito di
questo comando consiste nel generare un errore appropriato. La
sintassi d’uso di New-Item, dunque, può essere schematizzata così:
Nello script che segue, per esempio, si utilizza questo cmdlet per
creare dapprima una nuova cartella e poi un nuovo file di testo
all’interno di questa:
Directory: C:\Users\ikmju
HKEY_CURRENT_USER
Remove-Item
Questo cmdlet elimina gli elementi specificati tramite il parametro
posizionale -Path, che accetta anche i caratteri wildcard. Anche in
questo caso è possibile utilizzare lo switch -Recurse per eliminare tutti
gli elementi figlio di un percorso specifico e lo switch -Force per
imporre al cmdlet di eliminare anche gli elementi che altrimenti non
sarebbero presi in considerazione, come i file nascosti, di sistema o
in sola lettura. La sintassi di base di Remove-Item è:
Nello script che segue, per esempio, sono eliminati gli elementi
creati nel paragrafo precedente:
Rename-Item
Questo cmdlet si occupa di rinominare un elemento gestito da un
provider di PowerShell. Accetta il parametro posizionale -Path, tramite
il quale è possibile specificare l’elemento su cui si intende agire, e il
parametro -NewName, con cui se ne può indicare il nuovo nome. Lo
switch -Force, infine, consente di rinominare anche quegli elementi
che altrimenti non potrebbero essere modificati, come i file nascosti,
di sistema oppure in sola lettura. La sintassi di base necessaria a
eseguire Rename-Item è:
Directory: C:\Users\ikmju
PS C:\Users\ikmju> $x = 123
PS C:\Users\ikmju> "`$x -> $x, `$y -> $y"
$x -> 123, $y ->
PS C:\Users\ikmju> Rename-Item Variable:\x -NewName "y"
PS C:\Users\ikmju> "`$x -> $x, `$y -> $y"
$x -> , $y -> 123
Copy-Item
Il cmdlet copy-item consente di copiare un elemento da un percorso a
un altro, utilizzando lo stesso provider. Con il consueto parametro
posizionale -path si può indicare l’elemento oggetto della copia, con
l’eventuale supporto dei caratteri wildcard e la destinazione si
specifica per mezzo del parametro posizionale -Destination. L’impiego
dello switch -Recurse, inoltre, indica che la copia degli elementi deve
essere ricorsiva, prendendo in considerazione tutti gli elementi figlio
del percorso indicato (nel caso si tratti di un contenitore, come una
cartella del file system); lo switch -Force, infine, permette di
sovrascrivere gli elementi di destinazione in sola lettura, che
altrimenti genererebbero un errore appropriato. La sintassi di base di
Copy-Item è:
Directory: C:\Users\ikmju
Directory: C:\Users\ikmju
Gli alias predefiniti per Copy-item ricalcano gli omonimi comandi delle
precedenti shell Microsoft e di quelle di derivazione *nix: copy, cp.
Move-Item
Questo cmdlet sposta un elemento da un percorso a un altro, fermo
restando che questi siano entrambi gestiti dal medesimo provider.
Nel caso di directory, tuttavia, Move-Item è limitato allo spostamento
nell’ambito dello stesso drive di origine. Alla stregua di quanto
avviene per il comando precedente, con il parametro posizionale -
path si può indicare l’elemento oggetto dello spostamento, con
Hive: HKEY_CURRENT_USER\Software\TestKeyl
Hive: HKEY_CURRENT_USER\Software
Set-Item
Il cmdlet Set-Item è speculare rispetto a Get-item e permette di
impostare un particolare elemento individuato dal percorso fornito,
grazie al parametro posizionale -Path, rispetto al valore specificato
mediante il parametro posizionale -Value. Anche Set-item accetta lo
switch -Force per sovrascrivere gli elementi in sola lettura, operazione
che altrimenti genererebbe un errore.
La sintassi di base del comando è schematizzabile così:
Invoke-Item
Il cmdlet Invoke-Item esegue l’operazione predefinita associata al
percorso specificato tramite il parametro posizionale -Path. Nella
versione 2.0 di PowerShell questo comando è limitato al provider
Filesystem e recupera l’eventuale eseguibile associato al file indicato in
Invoke-Item <Percorso>
Get-ItemProperty
Grazie a questo cmdlet è possibile recuperare facilmente le proprietà
di un elemento in base al percorso indicato tramite il parametro
posizionale -path. Il parametro -Name, anche questo posizionale,
permette inoltre di limitare le proprietà ritornate dal comando per
mezzo di un array di stringhe. La sintassi di base di Get-ltemProperty è:
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\
SOFTWARE\Microsoft\Windows NT\CurrentVersion
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\
SOFTWARE\Microsoft\Windows NT
PSChildName : CurrentVersion
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
ProductName : Windows Server (R) 2008 Standard without Hyper-V
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\pagefile.sys
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\
PSChildName : pagefile.sys
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
LastWriteTime : 12/03/2010 18.12.18
Set-ltemProperty
Set-ItemProperty agisce in maniera speculare al comando
precedente e permette di impostare il valore di una proprietà di un
elemento, individuata specificando il percorso di quest’ultimo grazie
al parametro posizionale -Path e il nome della prima mediante il
parametro posizionale -Name. Il valore da impiegare, inoltre, è fornito
tramite il parametro -Value, anch’esso posizionale.
Il cmdlet, infine, ammette l’impiego dello switch -Force per
sovrascrivere le proprietà degli elementi in sola lettura. La sintassi di
base di Set-ItemProperty è:
Lo script che segue, per esempio, aggiunge alla barra del titolo di
Internet Explorer un testo (tipicamente utilizzato per fare branding)
sfruttando una particolare voce del registry di Windows:
New-ltemProperty
Il cmdlet New-itemProperty permette di creare una nuova proprietà di un
elemento, in maniera molto simile a quanto avviene con il comando
precedente. Il percorso dell’elemento si specifica tramite il parametro
posizionale -Path e il nome della proprietà per mezzo del parametro
posizionale -Name. Il valore da impiegare, inoltre, è fornito tramite il
parametro -Value. Anche questo cmdlet, infine, ammette l’impiego
dello switch -Force per creare proprietà negli elementi in sola lettura.
La sintassi di base di New-ItemProperty è riassumibile così:
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\
Software\Microsoft\Fusion
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\
Software\Microsoft
PSChildName : Fusion
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
EnableLog : 1
Remove-ItemProperty
Il cmdlet Remove-ItemProperty elimina una proprietà associata a un
elemento, in maniera speculare a quanto effettuato dal comando
precedente. L’elemento su cui si desidera intervenire si specifica
tramite il parametro posizionale -Path e il nome della proprietà da
eliminare per mezzo del parametro posizionale -Name. Utilizzando lo
switch -Force, infine, è possibile eliminare le proprietà degli elementi
in sola lettura, operazione che altrimenti genererebbe un errore.
Il cmdlet Remove-ItemProperty ha una sintassi di base riassumibile
mediante questo schema:
Nel blocco che segue, quindi, questo comando viene utilizzato per
rimuovere il valore aggiunto alla chiave del registry nell’esempio
precedente:
Rename-ItemProperty
Questo cmdlet reimposta il nome di una proprietà di un elemento
gestito da un provider di PowerShell. Utilizzando i parametri
posizionali -Path, -Name e -NewName è possibile specificare,
rispettivamente, il percorso dell’elemento di interesse, il nome della
proprietà da modificare e il nuovo nome da utilizzare. Lo switch -
Force, infine, può essere impiegato per rinominare una proprietà di un
elemento in sola lettura.
La sintassi di base di Rename-ItemProperty è:
Copy-ItemProperty
Questo cmdlet è in grado di copiare una proprietà da un elemento a
un altro. Il percorso dell’elemento di origine è specificato tramite il
parametro posizionale -Path, mentre quello di destinazione con il
parametro -Destination, anch’esso posizionale. Il nome della proprietà,
infine, è indicato grazie al parametro posizionale -Name, mentre con lo
switch -Force si permette al comando di sovrascrivere l’eventuale
proprietà nell’elemento di destinazione. La sintassi del cmdlet è
schematizzabile così:
Move-ItemProperty
si occupa di spostare una proprietà da un elemento a
Move-ItemProperty
Switch -WhatIf
Quando è specificato, questo utilissimo switch non esegue il cmdlet
attivamente, ma si limita a mostrare a video un messaggio che
descrive l’operazione che il comando avrebbe altrimenti effettuato.
Aggiungendo lo switch -WhatIf a un’operazione di spostamento, per
esempio, la shell visualizza un messaggio simile a questo senza
sortire alcun effetto sul file specificato:
Switch -Confirm
Lo switch -Confirm, infine, prima di eseguire il comando cui è
applicato mostra a video lo stesso messaggio descrittivo visualizzato
da -WhatIf e successivamente chiede conferma all’utente prima di
eseguire il cmdlet desiderato. La modalità con cui -Confirm richiede
conferma all’utente varia a seconda dell’interfaccia di PowerShell
utilizzata; le interfacce grafiche, solitamente, impiegano una finestra
modale a parte. Specificando lo switch -Confirm per l’eliminazione di
una chiave del registry di Windows, per esempio, l’interfaccia a
console testuale di PowerShell chiede conferma all’utente in un
modo simile a questo:
Conferma
Per l'elemento in HKLM:\SOFTWARE\Microsoft\Windows sono disponibili elementi
figlio e non è stato specificato il parametro Recurse. Se si continua, assieme
all'elemento verranno rimossi anche tutti gli elementi figlio. Continuare?
[S] Sì [T] Sì a tutti [N] No [U] No a tutti [O] Sospendi [?] Guida (il
valore predefinito è "S"):
Sia -WhatIf sia -Confirm, quindi, sono validi strumenti che permettono di
evitare possibili danni durante le normali attività di utilizzo di
PowerShell.
WMI
NO Per ulteriori dettagli su DCOM, RPC e l’eventuale gestione delle porte coinvolte
TA nella comunicazione con questi componenti si rimanda al sito Microsoft TechNet.
Recuperare le informazioni
Per recuperare e, eventualmente, interagire con la configurazione di
un determinato apparato (hardware o software) del sistema è
necessario conoscere il namespace e la classe su cui si desidera
operare all’interno di WMI.
Prima di analizzare gli strumenti messi a disposizione da
PowerShell, può essere utile dare una prima occhiata all’alberatura
dei namespace WMI all’interno di un sistema Windows utilizzando lo
snap-in MMC di WMI, seguendo questi passaggi:
1. lanciare il file wmimgmt.msc dalla finestra di dialogo Esegui di
Windows;
2. fare clic destro sull’elemento Controllo WMI (computer
locale) e selezionare la voce Proprietà (la voce Connetti a un
altro computer consente di verificare i namespace WMI
supportati da un’altra macchina, in remoto);
Figura 18.1 - Selezione della voce Proprietà dal menu contestuale dell’elemento Controllo
WMI.
Get-WmiObject
Quando si tratta di WMI, Get-WmiObject è il cmdlet principale di
PowerShell: grazie a una sintassi semplice ma ricca di funzionalità,
questo comando è in grado sia di recuperare le istanze di una
particolare classe WMI sia di fornire informazioni sulle classi e sui
namespace disponibili all’interno del sistema.
Mediante i parametri -Class (posizionale) e -Namespace è possibile
recuperare la lista delle istanze associate a una determinata classe
WMI, secondo questa sintassi:
Get-WmiObject [-Namespace <Namespace>] -Class <Classe>
ROOT\CIMV2.
__GENUS : 2
__CLASS : Win32_PhysicalMemory
__SUPERCLASS : CIM_PhysicalMemory
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_PhysicalMemory.Tag="Physical Memory 0"
__PROPERTY_COUNT : 30
[...]
NameSpace: ROOT\CIMV2
Name Methods Properties
---- ------- ----------
[...]
Win32_PrivilegesStatus {} {Description, Operation, ParameterInfo,
PrivilegesNotHeld...}
Win32_JobObjectStatus {} {AdditionalDescription, Description, Operation,
ParameterInfo...}
Win32_Trustee {} {Domain, Name, SID, SidLength...}
Win32_ACE {} {AccessMask, AceFlags, AceType,
GuidInheritedObjectType...}
Win32_SecurityDescriptor {} {ControlFlags, DACL, Group, Owner...}
Win32_ComputerSystemEvent {} {MachineName, SECURITY_DESCRIPTOR, TIME_CREATED}
Win32_ComputerShutdownEvent {} {MachineName, SECURITY_DESCRIPTOR, TIME_CREATED,
Type}
Win32_IP4RouteTableEvent {} {SECURITY_DESCRIPTOR, TIME_CREATED}
Win32_SystemTrace {} {SECURITY_DESCRIPTOR, TIME_CREATED}
Win32_ProcessTrace {} {PageDirectoryBase, ParentProcessID, ProcessID,
ProcessName...}
[...]
NumberOfBlocks : 266330112
BootPartition : True
Name : Disco #0, partizione #0
PrimaryPartition : True
Size : 136361017344
Index : 0
\\<Macchina>\<Namespace>:<Classe>.<Proprietà1>=<Valore1> [,<Proprietà2>=<Valore2>]
__PATH
------
\\ALKAID\root\cimv2:Win32_Printer.DeviceID="Send To OneNote 2007"
\\ALKAID\root\cimv2:Win32_Printer.DeviceID="Microsoft XPS Document Writer"
\\ALKAID\root\cimv2:Win32_Printer.DeviceID="HP LaserJet 1020"
\\ALKAID\root\cimv2:Win32_Printer.DeviceID="Fax"
\\ALKAID\root\cimv2:Win32_Printer.DeviceID="\\\\RING0\\HP LaserJet 1020"
Name
----
subscription
DEFAULT
CIMV2
Cli
nap
SECURITY
RSOP
WebAdministration
WMI
directory
Policy
Hardware
ServiceModel
Microsoft
aspnet
ServiceName : RTL8167
MACAddress : 00 :1A:5C:DF: 18 :7C
AdapterType : Ethernet 8 02.3
DevicelD : 7
Name : Realtek PCIe GBE Family Controller
NetworkAddresses :
Speed : 10000000C
SELECT Name
FROM Win32_UserAccount
WHERE Disabled = FALSE
Per eseguire una query WQL ci si può avvalere del parametro -Query
del cmdlet Get-WmiObject; in tal caso, d’altra parte, non è consentito
l’impiego del parametro -Class, mentre è ancora possibile specificare
il namespace desiderato utilizzando -Namespace (di default ROOT\CIMV2). In
tale scenario, dunque, la sintassi del cmdlet assume la seguente
connotazione:
__GENUS : 2
__CLASS : Win32_UserAccount
[...]
Name : Administrator
__GENUS : 2
__CLASS : Win32_UserAccount
[...]
Name : ikmju
NO Per ulteriori dettagli sulla sintassi WQL si rimanda al sito Microsoft TechNet
TA (http://technet.microsoft.com/it-it/default.aspx).
ExitCode : 0
Name : W32Time
ProcessId : 0
StartMode : Auto
State : Stopped
Status : OK
ExitCode : 0
Name : MMCSS
ProcessId : 0
StartMode : Auto
State : Stopped
Status : OK
[...]
Invoke-WmìMethod
Questo cmdlet permette di invocare un metodo WMI in base al
percorso dell’oggetto fornito per mezzo del parametro -path oppure di
-Class e -Namespace, seguendo la stessa logica di Get-WmiObject, a
prescindere dal fatto che il metodo sia d’istanza o statico. Il nome del
metodo è specificato tramite il parametro -Name, mentre l’array di valori
degli argomenti attesi dal metodo è fornito tramite il parametro -
ArgumentList. Rispetto alle diverse sintassi analizzate in precedenza,
[...]
ProcessId : 3636
ReturnValue : 0
[...]
ProcessId : 2080
ReturnValue : 0
[...]
ProcessId : 8309
ReturnValue : 0
Remove-WmiObject
Il cmdlet Remove-WmiObject consente di eliminare una particolare istanza
di un oggetto WMI, dove il concetto di eliminazione è interamente
delegato al provider associato all’oggetto. Nonostante sia possibile
specificare il path, il namespace, la classe e la macchina di
destinazione utilizzando, rispettivamente, i parametri -Path, -Namespace,
-Class e -ComputerName, questo comando è per lo più utilizzato all’interno
della pipeline, in seguito all’emissione della sequenza di oggetti che
si desidera eliminare. La sintassi utilizzata più di frequente, pertanto,
è individuata da questo semplice schema:
Set-WmiInstance
Il cmdlet Set-WmiInstance permette di reimpostare i valori delle proprietà
associate a una particolare istanza di un oggetto WMI, poiché la
modifica delle proprietà degli oggetti ritornati da Get-WmiObject non ha
effetto sugli oggetti WMI di destinazione. Anche in questo caso è
possibile specificare il path, il namespace, la classe e la macchina di
destinazione utilizzando, rispettivamente, i parametri -Path, -Namespace,
-Class e -ComputerName. Le proprietà da aggiornare, invece, sono fornite
[...]
DriveLetter : C:
[...]
Label : Disco rigido
[...]
[...]
DriveLetter : C:
[...]
Label : Disco rigido
[...]
COM
$type = [Type]::GetTypeFromCLSID(<CLSID>)
$object = [Activator]::CreateInstance($type)
TypeName: System.__ComObject#{866738b9-6cf2-4de8-8767-f794ebe74f4e}
PS C:\Users\ikmju> $shell.MinimizeAll()
PS C:\Users\ikmju> $shell.UndoMinimizeALL()
PS C:\Users\ikmju> $shell.FileRun()
La Guida di Windows:
PS C:\Users\ikmju> $shell.Help()
PS C:\Users\ikmju> $shell.Explore("C:\")
\\tsclient\C
Z:
PS C:\Users\ikmju> $network.RemoveNetworkDrive("F:")
filter Measure-WordDocument ()
{
BEGIN
{
$word = New-Object -ComObject Word.Application
$word.Visible = $false
}
PROCESS
{
$document = $word.Documents.Open($_.FullName)
$result
$document.Close()
}
END
{
$word.Quit()
}
}
Si è scelto di evitare di creare un nuovo oggetto COM a ogni iterazione del filtro
NO per non incorrere nel calo di performance che si verificherebbe altrimenti. La
TA gestione dell’oggetto Word, tra l’altro, non prende volutamente in considerazione
la possibilità di errore, per rendere più semplice e comprensibile lo script.
NO A partire dalla versione 3.5 del framework Microsoft .NET, le funzionalità relative
TA alle librerie Speech API sono disponibili all’interno del namespace System.Speech.
Le codifiche
Le sequenze di byte impiegate per memorizzare un testo all’interno
di un file variano in base alla codifica utilizzata, chiamata in gergo
encoding. La prima codifica storica è l’ASCII (American Standard
Code for Information Interchange), nata agli inizi degli anni ’60 per
consentire lo scambio di informazioni tra i primi calcolatori
dell’epoca; questo standard prevede che a ogni simbolo sia
associato un intero a 7 bit, per un totale di 128 possibili elementi.
Gli anni ’80 vedono la nascita di alcune estensioni del set ASCII
chiamate codepage, che prevedono l’impiego di un bit aggiuntivo
per rappresentare i simboli non contemplati dal modello originale,
basato sulla lingua inglese. Ogni codepage diventa in seguito uno
standard e la maggior parte viene raggruppata nel documento
ISO/IEC 8859. Le codifiche ASCII a 8 bit sono a tutt’oggi largamente
utilizzate per lo scambio di testi (come e-mail e pagine web)
attraverso Internet: la lingua italiana e quella inglese, per esempio,
sono entrambe contemplate dallo standard ISO/IEC 8859-1, che è
anche la codifica predefinita quando si recuperano contenuti testuali
da un server web. Nonostante l’abbondanza di codepage esistenti
all’epoca, a cavallo tra gli anni ’80 e ’90 diventa chiaro che il numero
di simboli rappresentabili dalle codifiche menzionate in precedenza
non è più sufficiente a soddisfare le necessità di globalizzazione e
localizzazione delle informazioni. Nasce così Unicode, un consorzio
senza fini di lucro il cui obiettivo è creare un database di codici
associati a tutti i simboli conosciuti, in modo indipendente dalla
lingua e dalla piattaforma utilizzata. La codifica principale prodotta
dal consorzio è chiamata UTF-16 (o, genericamente, Unicode) e
consente di memorizzare qualsiasi simbolo attualmente conosciuto
in una sequenza di due byte.
La codifica UTF-8 è una variante della precedente e consente di
generare sequenze di byte dalla dimensione variabile (da uno a sei
elementi) in base al simbolo: la maggior parte dei simboli presenti
anche in ISO 8859-1 (e, di conseguenza, in ASCII) è memorizzata,
tuttavia, in un unico byte.
All’interno del framework Microsoft .NET è possibile impiegare tutte
le codifiche supportate dal sistema operativo. Utilizzando la classe
System.Text.Encoding e i suoi membri statici è possibile recuperare
facilmente informazioni su di una particolare codifica e adoperarla
per generare sequenze di byte a partire da un testo e viceversa.
Il metodo statico GetEncodings(), per esempio, permette di recuperare la
lista delle codifiche supportate dal sistema, come illustrato dallo
script che segue:
Name DisplayName
---- -----------
IBM037 IBM EBCDIC (Stati Uniti-Canada)
IBM437 OEM Stati Uniti
[...]
IBM01144 IBM EBCDIC (Italia-Europa)
IBM01145 IBM EBCDIC (Spagna-Europa)
IBM01147 IBM EBCDIC (Francia-Europa)
[...]
utf-16 Unicode
windows-1250 Europa centrale (Windows)
windows-1251 Cirillico (Windows)
Windows-1252 Europa occidentale (Windows)
[...]
x-mac-japanese Giapponese (Macintosh)
x-mac-chinesetrad Cinese tradizionale (Macintosh)
x-mac-korean Coreano (Macintosh)
[...]
iso-8859-1 Europa occidentale (ISO)
iso-8859-2 Europa centrale (ISO)
iso-8859-3 Latin 3 (ISO)
iso-8859-4 Baltico (ISO)
iso-8859-5 Cirillico (ISO)
[...]
utf-7 Unicode (UTF-7)
utf-8 Unicode (UTF-8)
Fornendo al metodo statico GetEncoding() il nome della codifica
desiderata, inoltre, si ottiene un oggetto che la rappresenta, in grado
di elaborare le sequenze di byte associate ai simboli testuali.
Nello script che segue, per esempio, si recuperano tre codifiche
differenti (ISO-8859-1, UTF-8 e Unicode) e si utilizza il metodo
d’istanza GetBytes() per ottenere la sequenza di byte generata a
partire dal cognome di un importante fisico italiano del ’900. Lo script
si appoggia per comodità a una funzione Get-EncodedBytes(), definita
così:
Get-Content
Il cmdlet Get-Content permette di recuperare facilmente il contenuto di
un file, specificandone il nome per mezzo del parametro -Path,
posizionale. Questo comando considera sempre i file forniti come file
di testo e, nella maggior parte dei casi, è in grado di determinare
automaticamente la codifica utilizzata. Per forzare l’impiego di una
particolare codifica oppure per indicare a Get-Content di considerare il
documento un file binario ci si può avvalere del parametro -Encoding,
specificando uno dei valori ammessi dal comando: Unknown, String,
Unicode, Byte, BigEndianUnicode, UTF8, UTF7, Ascii. La codifica Byte in realtà
non esiste ma è un valore utilizzabile per forzare il cmdlet a
elaborare file binari. Come si può notare, tra l’altro, le codifiche
supportate direttamente dal comando sono molto limitate rispetto a
quelle offerte direttamente dal framework Microsoft .NET. La sintassi
di base del comando è rappresentabile da questo schema:
Get-Content <Path> [-Encoding <Codifica>]
Set-Content
A questo cmdlet è affidata la scrittura di file di testo e file binari, in
modo complementare a quanto effettuato dal comando precedente.
Il parametro posizionale -Path consente di indicare il nome del file su
cui si desidera operare, mentre con il parametro -Value, secondo
posizionale e utilizzabile via pipeline, si specifica il contenuto che
questo deve assumere. Il parametro -Encoding, poi, permette di variare
la codifica utilizzata per il salvataggio del file, in base agli stessi
valori supportati da Get-Content; lo switch -Force, infine, consente di
sovrascrivere file in sola lettura.
La sintassi di base del comando è questa:
Nello script che segue, per esempio, si usa Set-Content per creare (o
sostituire) un file di testo composto da una riga per ogni condivisione
di rete attiva nella macchina locale, facendo uso di WMI:
PS C:\Users\ikmju> Get-WmiObject Win32_Share | Select-Object -Expand Name | Set-
Content Shares.txt
Add-Content
Il cmdlet Add-Content è dotato di obiettivi e sintassi molto simili a Set-
Content e si differenzia da quest’ultimo perché, nel caso in cui venga
Clear-Content
Questo cmdlet è in grado di svuotare il contenuto di un file,
portandone la dimensione a zero byte. La sintassi del comando è
molto semplice e comprende la possibilità di specificare il parametro
posizionale -path, relativo al file su cui si desidera operare:
Clear-Content <Path>
Il cmdlet Clear-Content non elimina i file ma li svuota del solo contenuto. Per
NO
eliminare i file è necessario utilizzare il cmdlet Remove-Item, illustrato nel Capitolo
TA
17.
Out-File
Il cmdlet Out-File è uno dei comandi di output della pipeline, un
concetto analizzato nel Capitolo 9. Quando è presente questo
comando, per tutti gli oggetti emessi in precedenza nella pipeline
viene prodotta una rappresentazione testuale conforme al sistema di
visualizzazione di PowerShell e questa viene memorizzata all’interno
di un file. Per specificare il file di destinazione si può usare il
parametro -FilePath, posizionale, mentre attraverso il parametro -
Encoding si può indicare la codifica che Out-File deve utilizzare per
memorizzare il testo. A differenza dei cmdlet esposti in precedenza
in questa sezione, tuttavia, questo supporta solo file testuali e i valori
che -Encoding può assumere sono: Default, Unicode, BigEndianUnicode, UTF8,
UTF7, Ascii, UTF32, OEM.
Nello script che segue, per esempio, si sfrutta Out-File per raccogliere
l’output testuale di un comando all’interno di un file di testo:
Reindirizzare l’output
PowerShell gestisce il flusso di dati tra i comandi e l’host utilizzando i
canali standard (standard streams): una tecnologia nata in Unix per
rispondere all’esigenza di astrazione delle periferiche di input e di
output, oggi presente nella maggior parte dei sistemi operativi.
Nella terminologia dei canali standard, lo standard input (stdin) è il
canale da cui giunge il flusso di dati di ingresso, come il testo digitato
dall’utente nell’host. Lo standard output (stdout) è, invece, il canale
destinato a ospitare il risultato generato dal comando; lo standard
error (stderr), infine, è un canale di output destinato ad accogliere le
informazioni di diagnostica e gli errori generati dal comando.
In PowerShell il concetto di canale standard è stato parzialmente
rivisto per favorire il passaggio degli oggetti all’interno della pipeline:
in questo caso, infatti, non si tratta di semplice testo che fluisce dal
canale stdout di un comando allo stdin del successivo, ma di un
complesso flusso di oggetti strutturati che transitano all’interno della
pipeline. Al termine dell’elaborazione della pipeline, comunque,
l’eventuale rappresentazione testuale prodotta viene inoltrata al
canale stdout, mentre la rappresentazione testuale degli errori è
inoltrata al canale stderr.
Così come avviene nella maggior parte delle altre shell, anche in
PowerShell è possibile utilizzare alcune istruzioni in grado di
catturare il contenuto prodotto in un canale standard e memorizzarlo
altrove, evitando l’elaborazione predefinita (come la visualizzazione
a video). I cmdlet di output, esaminati nel Capitolo 9, permettono di
gestire accuratamente la produzione di risultati all’interno del canale
stdout; gli operatori di reindirizzamento, tuttavia, permettono di
governare in modo più organico la cattura dei canali standard e
offrono un’alternativa universalmente conosciuta rispetto all’impiego
dei cmdlet.
Operatore >
L’operatore > consente di ridirigere il canale stdout all’interno di un
file, in modo pressoché identico a quanto avviene con il cmdlet Out-
File. A differenza di quest’ultimo, tuttavia, la codifica è Unicode e non
può essere modificata. La sintassi per l’impiego di questo operatore
è schematizzabile così:
Directory: C:\Users\ikmju
Operatore >>
L’operatore » è una variante di quello precedente. Esso permette di
aggiungere il contenuto catturato dal canale stdout alla coda di un
file esistente, alla stregua di quanto è possibile ottenere specificando
lo switch -Append con il cmdlet Out-File.
La sintassi è molto simile a quella precedente:
Operatore 2>
L’operatore 2> permette di reindirizzare il canale stderr all’interno di
un file di testo, così come avviene per l’operatore >. La cifra 2, posta
a capo dell’operatore, deriva dall’implementazione C utilizzata per
dare vita alla tecnologia dei canali standard all’interno delle prime
versioni di Unix, dove i canali erano identificati dal loro indice
all’interno dell’array dei canali disponibili. La sintassi per l’impiego è
molto simile a quella dell’operatore >:
...2> <Path>
Reindirizzare il canale stderr può essere utile in tutti quei casi in cui
si desidera conservare la rappresentazione testuale degli errori
generati all’interno di un file, per elaborazione successive. Nello
script che segue, per esempio, si ridirigono gli errori prodotti da un
comando all’interno di un file di testo:
Operatore 2>>
Questo operatore di reindirizzamento è una variante del precedente.
Esso permette di aggiungere il contenuto catturato dal canale stderr
in coda a un file di testo esistente, così come avviene per l’operatore
». La sintassi di 2» è quindi molto semplice:
Operatore 2>&1
L’operatore 2>&1 indica alla shell di ridirigere automaticamente l’output
prodotto dal canale stderr all’interno del canale stdout. Da un punto
di vista pratico questo tipo di operatore è utilizzato quando si
desidera controllare tutto l’output prodotto da un comando - sia
risultati sia errori - utilizzando un unico canale. La sintassi di questo
operatore non prevede l’indicazione di alcun parametro accessorio
ed è rappresentata dal seguente schema:
... 2>&1
I processi
La gestione dei processi all’interno della shell è affidata ad alcuni
cmdlet, facilmente individuabili per via del sostantivo Process, incluso
nel loro nome.
Start-Process
Il cmdlet Start-Process permette di avviare un nuovo processo
ottenendo il relativo oggetto della classe System.Diagnostics.Process,
tramite il quale è successivamente possibile recuperare informazioni
e intervenire sul processo creato. Il parametro posizionale -FilePath è
utilizzabile in due modi: specificando direttamente il nome del file
eseguibile oppure indicando il nome del documento che si desidera
lanciare. In quest’ultimo caso la shell verifica nel registry quale sia
l’eseguibile principale associato al file, in base all’estensione di
quest’ultimo. Nel caso sia specificato lo switch -NoShellExecute, tuttavia,
il lancio di file che non siano eseguibili è inibito e genera un errore
appropriato.
Con il parametro posizionale -ArgumentList si forniscono eventuali
parametri all’eseguibile da lanciare, mentre con il parametro -verb si
può indicare l’eventuale verbo da utilizzare per lanciare il processo:
la lista dei verbi supportati da un particolare file è determinata in
base all’estensione di quest’ultimo e viene recuperata dal registry. La
sintassi di base di questo cmdlet è:
Get-Process
Grazie al cmdlet Get-Process è possibile recuperare facilmente un
oggetto system. Diagnostics.Process a partire da un processo esistente.
Mediante il parametro -ComputerName è possibile indicare al comando un
computer da cui ottenere le informazioni ricercate; quando non viene
specificato alcun valore, Get-Process utilizza la macchina locale.
Fornendo una o più stringhe al parametro posizionale -Name si impone
al cmdlet di filtrare i processi recuperati in base a questi valori, che
possono includere caratteri wildcard. In alternativa, specificando uno
o più valori interi tramite il parametro -Id, il filtro dei processi avviene
in base al loro identificativo (noto anche come PID, o Process
IDentifier). Se non è specificato nessuno di questi due parametri, il
comando ritorna tutti i processi.
La sintassi di base di questo cmdlet è rappresentabile così:
FileName
--------
C:\Windows\system32\conhost.exe
C:\Windows\system32\csrss.exe
C:\Windows\system32\csrss.exe
C:\Windows\system32\Dwm.exe
[...]
Stop-Process
Questo comando è in grado di terminare uno o più processi locali,
individuati per mezzo del loro identificativo tramite il parametro
posizionale -Id oppure fornendone il nome al parametro -Name.
L’impiego più frequente di questo cmdlet, tuttavia, consiste nel
fornire il processo di interesse direttamente tramite pipeline, sotto
forma di oggetto System.Diagnostics.Process.
La sintassi più semplice per eseguire Stop-Process, dunque, è questa:
Get-Process:
Wait-Process
Il cmdlet Wait-Process attende il termine di uno più processi locali,
individuati per mezzo del loro identificativo tramite il parametro
posizionale -Id oppure fornendone il nome al parametro -Name:
durante l’attesa del comando la shell non elabora nuove istruzioni e,
per interrompere l’esecuzione, è necessario utilizzare la
combinazione di tasti Ctrl+C. Come nel caso precedente, l’impiego
più frequente di questo cmdlet consiste nel fornire il processo di
interesse direttamente tramite pipeline, sotto forma di oggetto
System.Diagnostics.Process.
Mediante il parametro -Timeout, infine, si indica al comando un tempo
massimo di attesa (espresso in secondi) che, se superato senza
giungere al termine del processo desiderato, porta alla generazione
di un errore. La sintassi di base per l’impiego di questo cmdlet è la
seguente:
L’esecuzione della riga successiva riprende solo dopo che tutti i processi richiesti
NO
sono stati terminati. Per interrompere forzatamente l’attesa è possibile utilizzare la
TA
combinazione di tasti Ctrl+C.
I servizi
La gestione dei servizi Windows è una tra le attività più frequenti per
i sistemisti e i professionisti IT in genere: non solo questi elementi
sono alla base di qualsiasi funzionalità esposta dalle macchine con
un ruolo di server, ma il mancato funzionamento di un solo servizio
può compromettere lo stato dell’intero sistema. A livello tecnico, un
servizio non è nient’altro che un eseguibile progettato per rispondere
alle richieste di avvio, termine e sospensione generate dal Service
Control Manager (SCM) di Windows, il componente del sistema
operativo cui è affidata l’amministrazione dei servizi. Windows
utilizza il registry per memorizzare le informazioni su ogni servizio
installato nella macchina e, oltre al percorso del relativo eseguibile,
al nome con cui il servizio è individuabile e al nome da visualizzare
all’interno dell’interfaccia di gestione servizi di Windows, mantiene la
preferenza sul tipo di avvio, che può assumere uno di questi valori:
• Automatico: prevede che il servizio sia avviato
automaticamente al boot del sistema;
• Automatico (avvio ritardato): il servizio è avviato dopo il boot,
quando il sistema ha terminato di avviare gli altri servizi;
• Manuale: per avviare il servizio è necessario procedere
manualmente, utilizzando, per esempio, l’interfaccia di
amministrazione dei servizi di Windows;
• Disabilitato: il servizio non è avviabile.
Nel seguito di questo paragrafo sono analizzati i cmdlet principali
che PowerShell espone per gestire i servizi e il supporto offerto in tal
senso dal framework Microsoft .NET.
Get-Service
Il cmdlet Get-Service è in grado di recuperare informazioni dettagliate
su uno o più servizi locali o remoti, fornendone il nome al parametro
posizionale -Name; in alternativa, è possibile utilizzare il parametro -
DisplayName e specificare un filtro in base al nome visualizzato
Stop-Service
Il cmdlet Stop-Service ha un ruolo complementare al comando
precedente e arresta il servizio specificato tramite il parametro
posizionale -Name oppure, nel caso si fornisca il nome descrittivo,
tramite il parametro -DisplayName; anche in questo caso, d’altra parte, si
tende a sfruttare molto spesso la capacità del comando di accettare
oggetti di tipo System.ServiceProcess.ServiceController direttamente dalla
pipeline. La sintassi di base di Stop-Service è analoga a quella di Start-
Service e segue questo schema:
Suspend-Service e Resume-Service
Il cmdlet Suspend-Service è in grado di mettere in pausa un servizio,
mentre Resume-Service continua l’esecuzione di un servizio
precedentemente sospeso. Non tutti i servizi supportano il
meccanismo di sospensione – che, stando alle specifiche tecniche,
dovrebbe arrestare l’esecuzione mantenendo le eventuali risorse
caricate in memoria – ed è possibile far uso della proprietà logica
CanPauseAndContinue associata alle istanze di
System.ServiceProcess.ServiceController per determinarlo. Anche Suspend-
Restart-Service
Questo cmdlet si occupa di arrestare e successivamente avviare uno
o più servizi ed espone una sintassi identica a Start-Service, riepilogata
di seguito:
Set-Service
Il cmdlet Set-Service è in grado di modificare alcune informazioni su di
un servizio e, eventualmente, di cambiarne lo stato.
Il servizio su cui effettuare la modifica si specifica grazie al nome
identificativo e al nome della macchina – da fornire, rispettivamente,
tramite il parametro posizionale -Name e il parametro -ComputerName –
oppure per mezzo di un oggetto System.ServiceProcess.ServiceController,
direttamente tramite pipeline. Le informazioni da modificare sono
specificate grazie all’ausilio di alcuni parametri: -DisplayName permette
di assegnare un nuovo nome descrittivo al servizio, -StartupType
consente di modificarne il tipo di avvio e -Status cambia lo stato del
servizio (alla stregua di quanto è possibile fare con i cmdlet
analizzati in precedenza in questo paragrafo). La sintassi di base di
Set-Service è riepilogata da questo schema:
Recuperare le informazioni
Come il lettore potrebbe già sapere, un file XML è uno speciale
documento di testo, strutturato grazie alla presenza di simboli e
marcatori propri del metalinguaggio stesso. La trattazione dello
standard XML è al di fuori degli obiettivi di questo libro, ma vale la
pena ricordare come le informazioni codificate tramite questa
tecnologia siano organizzate gerarchicamente: ogni ramo della
struttura prende il nome di elemento e ogni elemento può essere
dotato di uno o più attributi, in grado di immagazzinare del testo.
Ogni elemento, poi, può contenere altri elementi ed eventualmente
contenere del testo esso stesso; alla base della struttura di un
documento XML, infine, c’è un elemento progenitore, chiamato root.
Il DOM
Il framework Microsoft .NET, così come la maggior parte delle
piattaforme di sviluppo, dedica alla lettura dei documenti XML un
intero modello di classi chiamato, genericamente, DOM (acronimo di
Document Object Model), per mezzo del quale le strutture XML sono
interamente caricate in memoria, riproducendo ogni nodo attraverso
un oggetto distintivo. Tutte le classi utilizzate dal framework per
rappresentare il modello DOM sono contenute nell’assembly
System.xml, all’interno del namespace omonimo.
L’approccio tipico di chi si appresta a utilizzare il supporto DOM
offerto dal framework Microsoft.NET con i linguaggi di sviluppo come
C# o Visual Basic .NET consiste nel creare una nuova istanza della
classe System.Xml.XmlDocument e nell’im-piegarne i metodi e le proprietà
per leggere il file desiderato e recuperare le informazioni ricercate.
PowerShell rende più semplice questo tipo di attività ed espone il
type accelerator [xml], che converte una stringa nell’equivalente
istanza di System.Xml.XmlDocument. Quando le caratteristiche del
documento lo consentono, inoltre, il supporto Extended Type System
della shell espone gli elementi del documento XML come proprietà
degli oggetti, così da rendere il codice più snello e leggibile. Per
caricare il documento di esempio Space.Xml tramite il type accelerator
[xml] e il cmdlet Get-Content, dunque, è possibile impiegare uno script
simile a questo:
name
----
Large Magellanic Cloud
Sagittarius Dwarf Sphr
Small Magellanic Cloud
Boötes Dwarf
Canis Major Dwarf
<Nodo>.SelectNodes(<Query XPath>)
<Nodo>.SelectSingleNode(<Query XPath>)
Select-Xml
Poiché la tecnologia XPath presenta una notevole utilità, a partire
dalla versione 2.0 PowerShell mette a disposizione dell’utente il
cmdlet Select-Xml, per elaborare query XPath a fronte di uno o più
documenti XML. La query è specificata tramite il parametro
posizionale -Xpath, mentre utilizzando il parametro -Content o fornendo
tramite pipeline una o più stringhe è possibile indicare al comando il
contenuto XML da processare. In alternativa, è possibile specificare
uno o più percorsi di file XML tramite il parametro -path, secondo
posizionale, oppure una o più istanze di nodi XML tramite il
parametro -Xml o direttamente via pipeline.
Le sintassi di base di Select-Xml sono rappresentate da questo
schema:
Nello script che segue, per esempio, si sfrutta questo cmdlet per
ricercare qualsiasi nodo XML che abbia un attributo type pari a Irr
all’interno di tutti i file con estensione .xml della cartella corrente:
PS C:\Users\ikmju> $space.Save("C:\Temp\Space2.xml")
Name
----
CreateAttribute
CreateCDataSection
CreateComment
CreateDocumentFragment
CreateDocumentType
CreateElement
CreateEntityReference
CreateNavigator
CreateNode
CreateProcessingInstruction
CreateSignificantWhitespace
CreateTextNode
CreateWhitespace
CreateXmlDeclaration
<Documento>-CreateElement(<NomeTag>)
<Documento>- CreateAttribute(<NomeAttributo>)
questo esempio:
Name : galaxy
LocalName : galaxy
[...]
ParentNode :
Name : type
LocalName : type
[...]
PS C:\Users\ikmju> $myGalaxy-OuterXml
<galaxy type="xyz" />
schema:
<Contenitore>.RemoveChild(<Elemento>)
<Contenitore>.RemoveAll()
<Elemento>.RemoveAttribute(<Nome attribùto>)
Trasformare i documenti
Lo standard XSLT, acronimo di eXtensible Stylesheet Language
Transformations, definisce un linguaggio in grado di trasformare un
documento XML in un altro file testuale, basando la trasformazione
su di un documento XML. Il framework Microsoft .NET offre un
ampio supporto per questa tecnologia e in questo paragrafo se ne
illustra brevemente un’applicazione pratica, pur evitando di trattare
direttamente il linguaggio XSLT, al di fuori degli obiettivi di questo
libro.
XSLT
All’interno del framework Microsoft .NET, le classi che consentono di
utilizzare la tecnologia XSLT sono concentrate nel namespace
System.Xml.Xsl, nell’assembly System.Xml. Tra tutte, la classe
probabilmente più utile al professionista che desidera effettuare una
trasformazione XSLT all’interno della shell èSystem.Xml.Xsl.XslTransform: le
istanze di questo tipo, infatti, espongono tutti i metodi necessari per
portare a termine questo tipo di attività.
La trasformazione si può dividere in due parti: posto di avere a
disposizione il file XML di trasformazione - per convenzione dotato di
estensione .xslt - è possibile richiedere all’istanza della classe
menzionata di caricarlo, utilizzando il metodo Load();
<xsl:template match="/">
<html>
<head>
<title>Galassie vicine</title>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="galaxy">
<b>
<xsl:value-of select="name/text()" />
</b>
Distanza: <xsl:value-of select="distance/text()" /> Mly
<br />
</xsl:template>
</xsl:stylesheet>
Utilizzando il supporto XSLT offerto dal framework Microsoft .NET è
possibile trasformare il documento di esempio Space.xml in un
documento in formato html, utilizzabile per la visualizzazione tramite
browser web. Lo script che segue dimostra come raggiungere
questo risultato:
Get-EventLog
Questo cmdlet è in grado di ottenere informazioni sui log e sugli
eventi di uno o più computer, limitandosi di default a quello locale.
Se lanciato utilizzando lo switch -List, il comando ritorna la lista dei
registri disponibili nella macchina, sotto forma di istanze della classe
System.Diagnostics.EventLog. Poiché il cmdlet non supporta la versione 6.0
cui gli eventi ritornati devono essere presenti, mentre -Newest ne limita
il numero totale; con il parametro -InstanceId si forniscono al comando
uno o più identificativi di istanza degli eventi desiderati, mentre con -
Message si può applicare un filtro direttamente al testo del messaggio
>> ForEach-Object {
>> $data = @{}
>>
>> $data.TimeGenerated = $_.TimeGenerated
>> $data.SecurityID = $_.ReplacementStrings[4]
>> $data.UserName = $_.ReplacementStrings[5]
>> $data.Domain = $_.ReplacementStrings[6]
>> $data.IPAddress = $_.ReplacementStrings[18]
>> $data.GetEnumerator()
>> }
>>
Name Value
---- -----
SecurityID S-1-5-18
Domain NT AUTHORITY
TimeGenerated 5/4/2010 4:41:58 PM
UserName SYSTEM
IPAddress -
SecurityID S-1-5-21-3959993727-2881392450-3707713393-1000
Domain Alkaid
TimeGenerated 5/4/2010 3:09:05 PM
UserName ikmju
IPAddress -
Per ulteriori informazioni sulla strutturazione dei dati all’interno della proprietà
NO
ReplacementStrings e la disponibilità degli stessi per ciascuna istanza di evento si
TA
rimanda al sito Microsoft TechNet (http://technet.microsoft.com).
Get-WìnEvent
Come anticipato nella prima parte del capitolo, questo cmdlet è in
grado di interfacciarsi con la più recente versione dell’infrastruttura di
registrazione degli eventi, disponibile a partire da Windows Vista.
Oltre a recuperare informazioni dai classici registri degli eventi in
modo analogo al precedente, il cmdlet Get-winEvent supporta la lettura
dei log creati utilizzando questa nuova tecnologia e i file creati dal
sistema Event Tracing for Windows (ETW).
NO Il cmdlet Get-WinEvent richiede la presenza del framework Microsoft .NET versione
TA 3.5 o superiore.
Nel blocco che segue, per esempio, si recuperano tutti gli eventi
memorizzati nel registro Microsoft-Windows-TaskScheduler/Operational del
computer locale, utilizzato dal Task Scheduler di Windows
(Operazioni pianificate, nella localizzazione italiana):
PS C:\Users\ikmju> Get-WinEvent Microsoft-Windows-TaskScheduler/Operational
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Service Control Manager" Guid="{555908d1-a6d7-4695-8e1e-
26931d2012f4}" EventSourceName="Service Control Manager" />
<EventID Qualifiers="16384">7036</EventID>
<Version>0</Version>
<Level>4</Level>
<Task>0</Task>
<Opcode>0</Opcode>
<Keywords>0x8080000000000000</Keywords>
<TimeCreated SystemTime="2010-05-13T08:28:11.992666100Z" />
<EventRecordID>2596</EventRecordID>
<Correlation />
<Execution ProcessID="596" ThreadID="1416" />
<Channel>System</Channel>
<Computer>Alkaid</Computer>
<Security />
</System>
<EventData>
<Data Name="param1">Application Experience</Data>
<Data Name="param2">stopped</Data>
<Binary>410065004C006F006F006B00750070005300760063002F0031000000</
Binary>
</EventData>
</Event>
*/System[(Level=1 or Level=2)]
Nello script che segue, per esempio, si filtrano tutti gli eventi critici
(Level pari a 1) del log Application relativi all’utente BOZOS\Tycho:
PS C:\Users\ikmju> Get-WinEvent -FilterHashTable ©{ LogName='Application';
Level=l; UserID='BOZOS\Tycho'}
Name LogLinks
---- --------
.NET Runtime {Application}
.NET Runtime Optimization Service {Application}
Application {Application}
Application Error {Application}
Application Hang {Application}
New-EventLog
Il cmdlet New-EventLog consente di creare un nuovo log degli eventi
oppure di registrare una nuova sorgente associata a un log. Il nome
del nuovo log si specifica attraverso il parametro posizionale -LogName,
mentre il nome della sorgente da registrare è indicato per mezzo del
parametro -source, anch’esso posizionale.
Nel suo impiego di base, la sintassi d’uso di questo comando può
essere schematizzata in questo modo:
New-EventLog <Nome Log> <Nome sorgente>
In questo script, per esempio, si impiega il comando per registrare la
sorgente powershell.it all’interno del log Application (Applicazione,
nella localizzazione italiana):
PS C:\Users\ikmju> New-EventLog -Log Application -Source powershell.it
Una volta creati, il nuovo log o la nuova sorgente sono pronti all’uso,
sia per il recupero delle informazioni sia per l’inserimento di nuovi
eventi.
Mediante il parametro -computerName, infine, è possibile richiede al
cmdlet di creare il nuovo log o la nuova sorgente in uno o più PC
differenti da quello locale, utilizzando il protocollo DCOM/RPC per
comunicare con gli host remoti.
Write-EventLog
Il cmdlet Write-EventLog è in grado di aggiungere un nuovo evento a un
log, utilizzando la piattaforma di registrazione classica, senza
supporto esplicito per XML. Il log di destinazione deve essere già
esistente e si specifica attraverso il parametro posizionale -LogName; la
sorgente dell’evento, invece, è fornita tramite il parametro -Source,
anch’esso posizionale, mentre l’identificativo è indicato tramite il
parametro posizionale -EventId. Il testo dell’evento, poi, si fornisce
attraverso il parametro posizionale -Message, mentre con il parametro -
EntryType è possibile specificare il tipo di voce dell’evento, in base ai
valori illustrati nella Tabella 23.1. Alla stregua degli altri comandi
dedicati al log degli eventi, infine, utilizzando il parametro -ComputerName
è possibile richiede al cmdlet di aggiungere il nuovo evento a uno o
più PC differenti da quello locale, utilizzando il protocollo
DCOM/RPC per comunicare con gli host remoti.
La sintassi di base di questo comando si può schematizzare così:
Write-EventLog <Nome Log> <Nome sorgente> <ID evento> [Tipo voce]
<Messaggio> [-ComputerName <Computer 1> [, <Computer 2>, ...]]
Limit-EventLog
Questo comando è in grado di intervenire sulla configurazione di uno
o più log degli eventi, modificandone la dimensione massima e la
modalità di overflow dei dati. Attraverso il parametro posizionale -
LogName si indicano al cmdlet uno o più log su cui agire; il parametro -
Remove-EventLog
Il cmdlet Remove-EventLog permette all’utente di eliminare una o più
sorgenti di registrazione di eventi oppure uno o più log degli eventi
da una o più macchine. La rimozione di questo tipo di oggetti è
un’attività molto rara, perciò l’impiego di questo comando è di
conseguenza poco frequente.
In modo simile a quanto avviene per New-EventLog, si può utilizzare il
parametro posizionale -LogName per fornire a questo comando il nome
di uno o più log su cui intervenire; il parametro -Source, inoltre,
consente di specificare uno o più sorgenti di evento da eliminare.
La sintassi d’uso del comando è:
Remove-EventLog <Log 1> [, <Log 2>, ...]
Remove-EventLog -Source <Sorgente 1> [, <Sorgente 2>, ...]
Status : Success
Address : 192.168.178.25
RoundtripTime : 8
Options : System.Net.NetworkInformation.PingOptions
Buffer : {97, 98, 99, 100...}
Il metodo Send() ritorna un oggetto System.Net.Networklnformation. PingReply,
da cui è possibile recuperare il risultato del ping e la latenza (in
millisecondi).
Talvolta il servizio di risposta alle richieste di echo ICMP è disabilitato per questioni
NO
di sicurezza: in tal caso l’applicazione ping e il metodo omonimo appena illustrato
TA
ritornano sempre un timeout della richiesta.
Utilizzare il protocollo HTTP
Il protocollo HTTP (acronimo di HyperText Transfer Protocol) è uno
degli standard fondamentali su cui si basa lo scambio di informazioni
sul web. Nonostante sia utilizzato in gran parte per la navigazione
web, la struttura di HTTP permette lo scambio di qualsiasi tipo di
dato e oggi è molto frequente imbattersi in servizi erogati tramite
questo protocollo e totalmente privi di interfaccia.
Nel corso di questo paragrafo sono analizzate le principali tecniche
di impiego di questo protocollo all’interno della shell.
title
-----
News / Community Tour 2010 - Cesena, 28 maggio 2010
Snippet / Visualizzare l'output pagina per pagina
News / Community Tour 2009 - San Vito al Tagliamento (PN), 4 dicembre 2009
News / Sessione online gratuita su Data Protection Manager
Snippet / Controllare se un IP è nelle black list DNSBL
Molti web service rispondono solo a richieste effettuate tramite il metodo POST del
NO protocollo HTTP (anziché GET): in tal caso è necessario utilizzare le classi esposte
TA dal framework Microsoft .NET per effettuare richieste HTTP più complesse, come
System-Net-WebRequest.
while ($true)
{
# Recupero del contesto HTTP
$httpContext = $httpListener.GetContext()
$httpResponse = $httpContext.Response
$httpResponse.Headers.Add("Content-Type", "text/html")
$httpResponse.Headers.Add("Server", "PowerShell/1.0")
$httpListener.Stop()
Questo script di esempio è stato pubblicato per la prima volta sul portale
NO
powershell.it e risponde a ogni richiesta pervenuta all’indirizzo http://localhost:
TA
1234 con un messaggio in formato HTML.
Interrogare il DNS
Per quanto riguarda il recupero di informazioni tramite il protocollo
DNS, il framework Microsoft .NET espone solo la classe System.Net.Dns,
in grado di effettuare interrogazioni DNS limitate ai record A, CNAME e
AAAA. Naturalmente, nel caso si rivelasse necessario utilizzare uno
Risoluzione DNS
La risoluzione di un nome DNS si avvale del metodo GetHostEntry()
della classe sopra citata, che restituisce una struttura con la lista
degli indirizzi IP e degli eventuali alias relativi al nome specificato. La
sintassi di chiamata segue questo semplice schema:
<Istanza Dns>.GetHostEntry(<Nome DNS>)
HostName : Ibi.www.ms-akadns.net
Aliases : {}
AddressList : {207.46-170.10, 207.46.170.123, 65.55.12.249}
NO Nel caso l’host specificato sia relativo a un record CNAME, la proprietà HostName
TA ritorna il nome DNS del relativo record A (o AAAA). ).
Count : 6
Average : 40.5
Sum :
Maximum : 45
Minimum : 38
Property : RoundtripTime
Inviare e-mail
Dalla versione 2.0 PowerShell include il cmdlet Send-MailMessage, in
grado di generare un’e-mail e di spedirla a un server SMTP,
gestendo in modo trasparente tutti i protocolli che intervengono in
questa attività.
Tramite il parametro -From si indica al comando l’indirizzo e-mail del
mittente, mentre con -TO si specificano i destinatari; l’oggetto e il
corpo del messaggio, invece, si forniscono rispettivamente tramite i
parametri -Subject e -Body. Il server SMTP da utilizzare, infine, è
specificato tramite il parametro -SmtpServer.
La sintassi di base del comando è dunque questa:
Send-MailMessage -From <Mittente> -To <Destinatariol> [, <Destinatario2>
...] -Subject <Oggetto> -Body <Messaggio> -SmtpServer <Server SMTP>
Name
--
ASCII
BigEndianUnicode
Default
Unicode
UTF32
UTF7
UTF8
NO La scelta della codifica UTF8 è dettata dalla preferenza di una codifica Unicode
TA che sia quanto più possibile snella (per i testi in lingua italiana).
Personalizzare l’ambiente
L’ambiente di esecuzione di PowerShell può essere modificato
tramite script per riflettere le esigenze e lo stile dell’utente; la shell,
infatti, espone attraverso la variabile automatica $Host diversi metodi
e proprietà che consentono di intervenire con facilità sui colori
utilizzati per visualizzare a video il testo, sul titolo della finestra
dell’host e su altre impostazioni che regolano la permanenza delle
informazioni visualizzate nel buffer video.
Tra tutte le proprietà esposte da $Host, quella forse più interessante
per gli obiettivi di questo capitolo è UI, che espone diversi metodi e
comprende, a sua volta, la proprietà RawUI, che permette di accedere
ai dettagli esposti dall’host corrente. Come di consueto, l’impiego del
cmdlet Get-Member consente di ottenere una visuale rapida dei membri
di questi oggetti; ecco qual è l’output del comando all’interno della
console testuale:
PS C:\Users\ikmju> $Host.UI | Get-Member | Select-Object Name, MemberType
Name MemberType
---- ----------
Equals Method
GetHashCode Method
GetType Method
Prompt Method
PromptForChoice Method
PromptForCredential Method
ReadLine Method
ReadLineAsSecureString Method
ToString Method
Write Method
WriteDebugLine Method
WriteErrorLine Method
WriteLine Method
WriteProgress Method
WriteVerboseLine Method
WriteWarningLine Method
RawUI Property
Name MemberType
---- ----------
Equals Method
FlushInputBuffer Method
GetBufferContents Method
GetHashCode Method
GetType Method
LengthInBufferCells Method
NewBufferCellArray Method
ReadKey Method
ScrollBufferContents Method
SetBufferContents Method
ToString Method
BackgroundColor Property
BufferSize Property
CursorPosition Property
CursorSize Property
ForegroundColor Property
KeyAvailable Property
MaxPhysicalWindowSize Property
MaxWindowSize Property
WindowPosition Property
WindowSize Property
WindowTitle Property
La console testuale
La classica console testuale di PowerShell permette di modificare i
colori utilizzati dall’host per visualizzare i testi modificando i valori
associati alle proprietà ForegroundColor e Backgroundcoior di $Host.UI.RawUI,
che corrispondono, rispettivamente al colore di primo piano e di
sfondo. I valori utilizzabili in ambedue i casi sono limitati per
questioni storiche ai i6 colori determinati dall’enumerazione system.
consoiecoior, che riflette la palette originariamente concepita per il
sistema grafico CGA. Nonostante questo limite sia stato superato a
partire da Windows Vista, gli elementi di System.Con.soleColor sono
rimasti invariati e in tutte le applicazioni a console testuale
compatibili con il framework Microsoft .NET (compresa la versione
4.0) il limite permane.
La variazione dei colori avviene solo per il testo visualizzato nella
finestra a console successivamente alle relative impostazioni e non
ha effetto sui dati pregressi: diversamente da quanto è possibile fare
con il tool a riga di comando COLOR all’interno di CMD, dunque, con
PowerShell è possibile visualizzare informazioni con qualsiasi
combinazione di colori tra quelli ammessi.
Nello script che segue, per esempio, si modifica lo stile della console
affinché visualizzi i testi dapprima utilizzando il colore rosso su
sfondo nero, poi bianco su sfondo magenta scuro (il predefinito della
shell):
PS C:\Users\ikmju> $Host.UI.RawUI.ForegroundColor = 'Red'
PS C:\Users\ikmju> $Host.UI.RawUI.BackgroundColor = 'Black'
PS C:\Users\ikmju> $Host.UI.RawUI.ForegroundColor = 'White'
PS C:\Users\ikmju> $Host.UI.RawUI.BackgroundColor = 'DarkMagenta'
PowerShell ISE
L’ambiente grafico di PowerShell supporta tutte le opzioni utilizzabili
nella controparte testuale mediante l’oggetto $Host.UI.RawUI. Gli script
eseguiti all’interno di PowerShell ISE, inoltre, dispongono di
un’ulteriore variabile automatica, chiamata $psISE, mediante la quale
questo ambiente espone una ricca struttura a oggetti che permette di
recuperare e manipolare le diverse componenti dell’interfaccia.
La proprietà CurrentFile di $psISE ritorna svariate informazioni sul file
associato al tab corrente, se presente, come il nome completo del
documento attraverso la proprietà FullPath e la codifica utilizzata, per
mezzo della proprietà Encoding. Il metodo Save() e i suoi overload,
inoltre, permettono di memorizzare il file programmaticamente.
La proprietà Editor è con tutta probabilità la più importante tra quelle
esposte da currentFiie e consente, invece, di recuperare preziose
informazioni sul testo del file, sull’eventuale selezione e sulla
posizione del cursore: i metodi di Editor, infine, permettono di
intervenire facilmente sul contenuto del documento, sulla posizione
del cursore, sulle selezioni e sull’area visualizzata dall’interfaccia.
Nello script che segue, per esempio, si visualizzano le informazioni
relative al nome del file corrente e alla riga del cursore al suo
interno:
PS C:\Users\ikmju> $psISE.CurrentFiie.FullPath, $psISE.CurrentFiie.Editor.CaretLine
C:\Users\ikmju\Desktop\WmiExplorer.psl
97
Name MemberType
---- ----------
PropertyChanged Event
Equals Method
GetHashCode Method
GetType Method
RestoreDefaults Method
RestoreDefaultTokenColors Method
ToString Method
CommandPaneBackgroundColor Property
CommandPaneUp Property
DebugBackgroundColor Property
DebugForegroundColor Property
DefaultOptions Property
ErrorBackgroundColor Property
ErrorForegroundColor Property
FontName Property
FontSize Property
OutputPaneBackgroundColor Property
OutputPaneForegroundColor Property
OutputPaneTextBackgroundColor Property
ScriptPaneBackgroundColor Property
ScriptPaneForegroundColor Property
SelectedScriptPaneState Property
ShowToolBar Property
ShowWarningBeforeSavingOnRun Property
ShowWarningForDuplicateFiles Property
TokenColors Property
UseLocalHelp Property
VerboseBackgroundColor Property
VerboseForegroundColor Property
WarningBackgroundColor Property
WarningForegroundColor Property
Il profilo utente
Il profilo utente di PowerShell è uno script che la shell carica ed
esegue all’avvio di ogni sessione e prima dell’esecuzione di qualsiasi
altro comando, dove è possibile definire le opzioni della shell,
caricare snap-in e definire alias, funzioni, provider e variabili che si
desidera siano sempre disponibili.
Il nome e il percorso del file di profilo sono tipicamente determinati in
base all’utente che lancia la shell e all’interfaccia impiegata ma, in
linea di massima, ogni host può utilizzare regole differenti: per
qualsiasi prodotto, tuttavia, il nome del file di profilo è disponibile
attraverso la variabile automatica $PROFILE.
L’interfaccia a console testuale di PowerShell utilizza il file
Microsoft.PowerShell_profi-le.ps1, all’interno della cartella WindowsPowerShell
Customizzare il prompt
Il testo visualizzato dal prompt dei comandi della shell è determinato
dalla funzione prompt(), che nell’implementazione predefinita ritorna
alcune informazioni sull’ambiente corrente. Richiamandone la
definizione tramite la shell, infatti, è possibile osservarne lo script:
PS C:\Users\ikmju> $function:prompt
$(if (test-path variable:/PSDebugContext) { '[DBG]: ' } else { '' }) + 'PS
+ $(Get-Location) + $(if el -gè 1) { '>>' }) + '>
$added = $svnOutput.SelectNodes("//wc-status[@item='added']").Count
$deleted = $svnOutput.SelectNodes("//wc-status[@item='deleted']").Count
$modified = $svnOutput.SelectNodes("//wc-status[@item='modified']").Count
$unversioned = $svnOutput.SelectNodes("//wc-
status[@item='unversioned']").Count
# Output manuale del testo del prompt, colorato nella sezione dedicata a
SubVersion
$svnCommands = 'add',
'blame',
'cat',
'changelist',
'checkout',
'cleanup'
if ($filter) {
# Comando parziale
$svnCommands |
Where-Object { $_.StartsWith($filter) } |
Sort-Object
}
else {
# Nessun comando
$svnCommands
}
}
}
I job
I job in background – da qui in avanti, per semplicità, job – sono
script di PowerShell eseguiti in sessioni indipendenti da quella
corrente, all’interno di nuovi processi della shell. L’esecuzione di ogni
job avviene in parallelo rispetto agli altri e alla sessione corrente,
consentendo all’utente di organizzare i propri script affinché il tempo
necessario all’esecuzione sia minimizzato e, complessivamente,
aumenti il grado di automazione dell’intervento. Come si può intuire,
esiste un forte isolamento tra la sessione corrente e quelle create
per i job, ma il sistema permette comunque alla prima di gestire le
seconde e di ottenerne gli eventuali risultati prodotti, attraverso
alcuni cmdlet specifici cui è dedicato il seguito del capitolo.
L’intera infrastruttura dei job si appoggia sul servizio Windows
Remote Management (Gestione remota Windows, nei sistemi in
lingua italiana), più noto con il semplice acronimo WinRM, che, a
dispetto del nome fuorviante, si occupa di gestire questo tipo di
attività sia per i job remoti sia per quelli locali, in modo centralizzato
e indipendente dall’utente connesso. Come anticipato nei capitoli
precedenti, WinRM è l’implementazione Microsoft del protocollo WS-
Management e permette lo scambio di informazioni – tra cui i dati
relativi ai job – tramite il protocollo SOAP (Simple Object Access
Protocol), sulla base di HTTP o HTTPS.
La presenza e la corretta configurazione di WinRM, pertanto, sono
requisiti fondamentali per il supporto dei job in background.
La configurazione di WinRM
L’installazione di WinRM varia in base al sistema operativo di
destinazione ed è illustrata nel Capitolo 1. In questo paragrafo,
pertanto, ci si limita ad analizzare la configurazione di questa
piattaforma dando per scontato che il pacchetto sia già presente nel
sistema.
Per utilizzare il supporto offerto da WinRM è necessario che il
relativo servizio sia avviato, che siano stati registrati correttamente
gli endpoint utilizzati dalla piattaforma per ricevere comandi tramite il
protocollo SOAP e che siano stati configurati senza errori i permessi
necessari. WinRM è una tecnologia indipendente da PowerShell e,
come ci si potrebbe aspettare, è possibile procedere alla
configurazione di ogni elemento richiesto tramite un’interfaccia
grafica. Il pacchetto d’installazione di WinRM, inoltre, include il tool a
riga di comando winrm.cmd che, interfacciandosi con l’oggetto COM
preposto alla gestione di questa piattaforma, ne permette una
semplice ma completa configurazione.
All’interno di PowerShell, tuttavia, per configurare WinRM è
sufficiente utilizzare il cmdlet Set-WSManQuickConfig, che prima di
proseguire visualizza a video un riepilogo delle attività da portare a
termine e chiede conferma all’utente, come illustrato nel blocco che
segue:
PS C:\Windows\system32> Set-WSManQuickConfig
wsmid :
http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor : Microsoft Corporation
ProductVersion : OS: 0.0.0 SP: 0.0 Stack: 2.0
L’avvio
Per creare e avviare un nuovo job è sufficiente utilizzare il cmdlet
Start-Job, fornendo tramite il parametro posizionale -ScriptBlock il blocco
di script da eseguire.
Nel suo impiego più semplice, dunque, questo comando utilizza la
sintassi che segue:
Start-Job { <Comandi> }
creato.
Nello script che segue, per esempio, si crea un nuovo job, tramite il
quale si emette una stringa nella pipeline:
PS C:\Users\ikmju> Start-Job { Write-Host "hello, world" }
Nello script che segue, per esempio, si esegue un nuovo job che
ritorna il nome del computer locale e si recupera il risultato:
PS C:\Users\ikmju> $job = Start-Job { $env:COMPUTERNAME }
PS C:\Users\ikmju> $job | Receive-Job
ALKAID
Come anticipato, d’altra parte, l’istante in cui i risultati del job sono
disponibili non è esattamente prevedibile, perché il flusso delle
informazioni è asincrono: per quanto lo script del job dell’esempio
precedente sia semplice, infatti, nulla assicura che la successiva
chiamata a Receive-Job ritorni effettivamente il risultato sperato. In tal
caso è necessario verificare la proprietà HasMoreData del job: quando
equivale a $true significa che Receive-Job potrebbe ritornare dei dati; in
caso contrario il job non è più attivo e le informazioni prodotte sono
già state prelevate.
Il lettore noti che, come anticipato, le informazioni ricevute da un job
transitano attraverso WinRM e, per tale ragione, possono essere
recuperate un po’ alla volta: generalmente conviene leggere i risultati
dei job all’interno di un ciclo, fino a quando la proprietà HasMoreData del
job rimane pari a $true.
Nello script che segue, per esempio, si ricavano i risultati di un job
che recupera i processi in esecuzione tramite un ciclo, come
descritto in precedenza:
PS C:\Users\ikmju> $job = Start-Job { Get-Process }
PS C:\Users\ikmju> while ($job.HasMoreData) {
>> $job | Receive-Job
>> }
>>
Oggetti deserializzati
Come anticipato nella prima parte del capitolo, il flusso di
informazioni prodotte dai job e recuperate tramite il cmdlet Receive-Job
viaggia attraverso il protocollo WS-Management/WinRM. Poiché si
tratta di un protocollo basato su XML, PowerShell utilizza una
tecnica nota come serializzazione per produrre rappresentazioni
molto accurate degli oggetti originali. D’altra parte, alcune
informazioni sono di pertinenza esclusiva della macchina di origine –
come per esempio l’identificativo di un processo o un SID (acronimo
di Security Identifier) di un utente locale - e possono avere un valore
limitato, giunte a destinazione. I metodi dell’oggetto di origine,
invece, potrebbero non avere una corrispondenza nella macchina di
destinazione e per questo sono omessi dalla rappresentazione
utilizzata da WinRM per descrivere gli oggetti.
Dato che queste limitazioni non sono compatibili con il resto
dell’ecosistema di oggetti del framework Microsoft .NET, quando
Receive-Job ricostruisce gli oggetti provenienti da WinRM all’interno di
TypeName: Deserialized.System.Diagnostics.Process
TypeName: System.Double
Sia per i tipi primitivi sia per quelli che non lo sono, infine, durante il
processo di deserializzazione l’ETS di PowerShell aggiunge a ogni
oggetto le proprietà PSComputerName e Runspaceld, utilizzabili dall’utente
per determinare quale sia la macchina (e la sessione) di provenienza
dell’elemento.
La gestione
La gestione dei job in PowerShell è così semplice che a questa
attività è dedicato un unico cmdlet, Get-Job. Per recuperare i job di
interesse è sufficiente fornire a questo comando gli identificativi
numerici o i nomi descrittivi degli elementi cercati, attraverso i
parametri posizionali -Id o -Name. In entrambi i casi, d’altra parte, il
binding tramite pipeline dei parametri avviene solo per nome di
proprietà.
La sintassi d’uso più semplice del comando è:
Get-Job -Id <Identificativo1> [, <Identificativo2>, ...]
Get-Job -Name <Nome1> [, <Nome2>, ...]
Se non si specifica alcun parametro, il cmdlet ritorna tutti i job creati
nella sessione corrente.
Nello script che segue, per esempio, si può constatare come il
cmdlet Get-job restituisca solo i job con il nome descrittivo specificato,
omettendo il terzo elemento creato:
PS C:\Users\ikmju> $job1 = Start-Job -Name Test { Write-Host "hello" }
PS C:\Users\ikmju> $job2 = Start-Job -Name Test { Write-Host "world" }
PS C:\Users\ikmju> $job3 = Start-Job { Write-Host "bye bye" }
PS C:\Users\ikmju>
PS C:\Users\ikmju> Get-Job -Name Test
Il cmdlet Get-Job può ritornare solo i job creati nella sessione corrente, ?51 anche
NO
se nella macchina locale sono attive altre sessioni di PowerShell gestite tramite
TA
WinRM.
Interruzione ed eliminazione
Per interrompere l’esecuzione di un job è sufficiente utilizzare il
cmdlet Stop-Job, individuando l’elemento (o gli elementi) di interesse
attraverso i consueti parametri posizionali -Job, -Id e -Name, fornendo,
rispettivamente, l’oggetto restituito da Start-Job, l’identificativo
numerico oppure il nome descrittivo. Il cmdlet accetta anche gli
oggetti su cui si desidera operare direttamente tramite pipeline.
La sintassi d’uso più semplice, pertanto, è rappresentata da questo
schema:
Stop-Job <Oggetto job1> [, <Oggetto job2>, ...]
Stop-Job <Identificativo1> [, <Identificativo2>, ...]
Stop-Job <Nome1> [, <Nome2>, ...]
<Oggetto job1> [, <Oggetto job2>, ...] | Stop-Job
Lo script che segue, per esempio, interrompe l’esecuzione di tutti i
job in background avviati nella sessione corrente:
PS C:\Users\ikmju> Get-Job | Stop-Job
Nello script che segue, per esempio, si creano tre diversi job
(sopprimendo l’output tramite il cmdlet Out-Null, per una maggiore
leggibilità). Attraverso la pipeline prodotta da Get-Job, Stop-Job e Remove-
Job si recuperano tutti i job prodotti nella sessione, si interrompono e,
infine, si eliminano:
PS C:\Users\ikmju> Start-Job { Start-Sleep 100 } | Out-Null
PS C:\Users\ikmju> Start-Job { Start-Sleep 200 } | Out-Null
PS C:\Users\ikmju> Start-Job { Start-Sleep 300 } | Out-Null
PS C:\Users\ikmju>
PS C:\Users\ikmju> Get-Job | Stop-Job -PassThru | Remove-Job
Fino a questo punto del libro sono stati analizzati alcuni cmdlet in
grado di operare in macchine remote utilizzando le API del sistema
operativo e la tecnologia DCOM/RPC. Nonostante l’enfasi con cui fu
inizialmente introdotta, questa tecnologia presenta alcuni limiti,
dovuti per lo più alle difficoltà di configurazione dei firewall rispetto al
protocollo di comunicazione.
A partire dalla versione 2.0, PowerShell è in grado di utilizzare
Windows Remote Management e di far convogliare qualsiasi attività
di amministrazione remota attraverso il protocollo HTTP (o HTTPS),
colmando il gap della versione precedente e dei cmd-let basati su
DCOM/RPC. Diversamente da quando si usano questi ultimi, d’altra
parte, sulle macchine in cui si desidera operare da remoto è
necessario che PowerShell 2.0 sia installato e che il servizio di
WinRM sia configurato e avviato correttamente.
In questo capitolo sono discusse le tecniche di esecuzione di task in
remoto nella shell e i principali cmdlet che ne permettono la
completa gestione.
La configurazione di WinRM
Nelle macchine su cui si intende operare da remoto, dunque, è
necessario procedere alla configurazione e all’avvio del servizio
Windows Remote Management (Gestione remota Windows), alla
prenotazione dell’endpoint utilizzato dall’infrastruttura e
all’abilitazione del firewall di Windows. Così come per i job in
background, il cmdlet da utilizzare per portare a termine queste
attività è Set-WSManQuickConfig.
NOT Il cmdlet Set-WSManQuickConfig è stato introdotto nel Capitolo
A 26.
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Listener
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Listener\
Listener_1184937132
NO Per navigare all’interno del provider wsMan è necessario eseguire PowerShell con
TA i privilegi di amministrazione.
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Client
wsmid : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor : Microsoft Corporation
ProductVersion : OS: 0.0.0 SP: 0.0 Stack: 2.0
Invoke-Command
Nel suo utilizzo di base, questo cmdlet consente di eseguire un
blocco di script di PowerShell all’interno di uno o più PC remoti e
restituisce gli eventuali risultati nella pipeline. Gli host di destinazione
sono forniti al comando per mezzo del parametro posizionale -
computerName, mentre il blocco da eseguire è indicato per mezzo del
Directory: Microsoft.PowerShell.Core\FileSystem::C:\
Esecuzione in background
L’esecuzione di un’attività in remoto può essere facilmente convertita
in un job, grazie allo switch -Asjob del cmdlet Invoke-Command: in tal caso il
comando non ritorna direttamente il risultato del codice fornito, ma
restituisce un oggetto job creato all’interno della sessione locale, che
si può gestire con le tecniche illustrate nel capitolo precedente.
In questo script, per esempio, si crea un job a partire da un’attività
eseguita in remoto; l’impiego del job permette di liberare la sessione
corrente dall’attesa del termine dell’operazione:
PS C:\Users\ikmju> $job = Invoke-Command 192.168.178.25 { Get-Childltem C:\
-Recurse } -AsJob
Nel caso in cui lo switch -AsJob sia usato a fronte di più host remoti, il
cmdlet Invoke-Command ritorna un insieme di job subordinati a un job
principale, che funge da collettore. I job subordinati sono tanti quanti
gli host specificati e sono disponibili attraverso la proprietà ChildJobs
del job principale.
Lo script che segue, per esempio, illustra alcune informazioni sui job
creati a fronte dell’esecuzione remota di un’attività in background su
host multipli:
PS C:\Users\ikmju> $job = Invoke-Command 192.168.178.25, BOZOS, Inexistant { pwd } -
AsJob
PS C:\Users\ikmju> $job
Id Name State HasMoreData Location
Command
-- ---- ----- ----------- -------- -
------
13 Job13 Running True 192.168.178.25, BO...
pwd
Credential).
L’esecuzione dei comandi in remoto, pertanto, è soggetta al criterio
di esecuzione impostato nella macchina di destinazione ed è limitata
ai cmdlet e agli assembly disponibili in loco. Il profilo di PowerShell,
inoltre, è quello della macchina remota, quindi qualsiasi informazione
presente nel profilo locale, come gli eventuali alias e le funzioni
custom, potrebbe non trovare un elemento corrispondente nel profilo
caricato in remoto.
Come regola generale, quindi, quando si opera in remoto è sempre
conveniente utilizzare i nomi completi dei cmdlet oppure gli eventuali
alias predefiniti, ma evitare quelli che non lo sono e le funzioni
custom.
Sessioni remote
Le tecniche dedicate all’esecuzione di task remoti esposte fino a
questo punto presentano un limite, facilmente riscontrabile non
appena si desidera legare più comandi tra loro. Poiché il cmdlet
Ivoke-Command porta, di default, alla creazione di una nuova sessione per
New-PSSessìon
Il cmdlet New-Pssession crea una nuova sessione remota (l’acronimo
PSSession sta per PowerShell Session) verso un particolare host e
la ritorna nella pipeline. In modo molto simile a Invoke-Command, questo
comando permette di specificare il computer di destinazione tramite
il parametro posizionale -ComputerName. Anche in questo caso è
possibile agire sul parametro -Port e sullo switch -UseSSL per
modificare le impostazioni di base sulla connessione, mentre il
parametro -Credential è usato ancora una volta per connettersi alla
macchina remota utilizzando un utente differente da quello corrente.
In modo simile ai job, inoltre, le sessioni possono avere un nome
descrittivo per gestirle facilmente, da fornire tramite il parametro -
Name.
New-PSSession <Host>
New-PSSession <Host> [-Credential <Utente>] [-UseSSL] [-Port <Porta>] [-Name
<Nome descrittivo>[
Nel blocco che segue, per esempio, si utilizza questo cmdlet per
avviare una sessione remota, di cui PowerShell provvede poi a
mostrare a video le informazioni più importanti:
PS C:\Users\ikmju> New-PSSession 192.168.178.25 -Name "Sessione di test"
Get-PSSession
Questo cmdlet è utilizzato per recuperare le sessioni remote create
all’interno della sessione corrente, ritornando gli elementi
memorizzati internamente da PowerShell all’atto della creazione di
questi oggetti. Richiamando il comando senza specificare alcun
parametro si ottiene la lista di tutte le sessioni. Mediante il parametro
posizionale -ComputerName, invece, è possibile filtrare i risultati in base a
una o più destinazioni, mentre col parametro posizionale -Id si
recuperano le sessioni dotate degli identificativi univoci indicati. Il
parametro -Name, infine, è impiegato per filtrare i risultati in base a uno
o più nomi descrittivi.
La sintassi di base di questo comando segue dunque questo
semplice schema:
Get-PSSession
Get-PSSession <Host1> [, <Host2>, ...]
Get-PSSession <Id1> [, <Id2>, ...]
Get-PSSession -Name <Nome descrittivo1> [, <Nome descrittivo2>, ...]
ComputerName : 192.168.178.25
ConfigurationName : Microsoft.PowerShell
InstanceId : 2ede369d-a6a4-42bc-b118-26f9f9c1243d
Id : 8
Name : Sessione di test
[...]
Remove-PSSessìon
Come ci si potrebbe immaginare, le sessioni remote impegnano
risorse sia nella macchina che origina i comandi sia, soprattutto, in
quella che li esegue. Nonostante esista un sistema di eliminazione
automatica che interviene in caso di disconnessione, una volta
terminato di utilizzare una sessione conviene sempre procedere
all’eliminazione della stessa mediante il cmdlet Remove-Pssession. Come
nel caso di Get-Pssession, è possibile indicare a questo comando le
sessioni da rimuovere in base al nome del-l’host remoto,
all’identificativo univoco e al nome descrittivo utilizzando,
rispettivamente, i parametri -ComputerName, -Id e -Name. L’impiego più
frequente, tuttavia, consiste nell’indicare a Remove-PSSession le sessioni
desiderate tramite il parametro posizionale -Session oppure
direttamente mediante la pipeline.
La sintassi d’uso di base di questo cmdlet segue questo schema:
Remove-PSSession <Host1> [, <Host2>, ...]
Remove-PSSession <Id1> [, <Id2>, ...]
Remove-PSSession -Name <Nome descrittivo1> [, <Nome descrittivo2>, ...]
Remove-PSSession -Session <Sessione1> [, <Sessione2>, ...]
<Sessione1> [, <Sessione2>, ...] | Remove-PSSession
NO Nel caso non esistano sessioni associate all’host specificato, il comando genera
TA un errore appropriato.
Enter-PSSession
Il cmdlet Enter-Pssession consente di avviare una sessione remota
interattiva operando all’interno di una sessione esistente oppure
creandone una ex novo.
Per utilizzare una sessione esistente è sufficiente fornire al comando
l’elemento di interesse attraverso il parametro posizionale -Session,
direttamente tramite la pipeline oppure, ancora, specificando
l’identificativo univoco mediante il parametro -Id. Per creare una
nuova sessione, invece, è possibile utilizzare lo stesso set di
parametri illustrati in precedenza per New-Pssession: -ComputerName per
specificare l’host di destinazione, -port per indicare la porta, lo switch
-UseSSL per impiegare il protocollo HTTPS e -Credential per usare
Enter-PSSession <Host>
New-PSSession <Host> [-Credential <Utente>] [-UseSSL] [-Port <Porta>]
Exìt-PSSessìon
Per terminare una sessione remota interattiva è sufficiente usare il
cmdlet Exit-PSSession senza fornire alcun parametro. Se la sessione
remota era già esistente al momento della chiamata di Enter-PSSession,
questo comando si limita a uscire dalla modalità di esecuzione
interattiva, ma non agisce sulla sessione remota, che può essere
riutilizzata. Nel caso in cui, invece, la sessione remota sia stata
creata direttamente da Enter-PSSession, il cmdlet Exit-PSSession si occupa
anche della sua chiusura.
Una volta terminata la sessione remota interattiva, il prompt di
PowerShell ritorna automaticamente allo stato assunto in
precedenza. Lo script che segue, continuazione dell’esempio
precedente, dimostra come la sessione remota sia ancora
utilizzabile dopo il termine della modalità interattiva e come sia
necessario chiuderla esplicitamente con Remove-PSSession:
[192.168.178.25]: PS C:\Users\ikmju> Exit-PSSession
PS C:\Users\ikmju> Invoke-Command $session { [Environment]::OSVersion.
VersionString }
Microsoft Windows NT 6.0.6001 Service Pack 1
PS C:\Users\ikmju> $session | Remove-PSSession
Errori sintattici
Prima di poter eseguire le istruzioni contenute in uno script o in una
semplice riga di codice, PowerShell ne effettua il parsing,
convertendone il testo in una rappresentazione simbolica in grado di
essere eseguita dal motore della shell. Durante questa fase il testo
viene sottoposto a complesse verifiche che permettono di validarne
la correttezza formale. Nel caso venga rilevato un errore di sintassi,
il comportamento predefinito della shell consiste nel mostrare a
video un messaggio descrittivo del problema e interrompere
l’esecuzione.
Disponendo di uno script esterno chiamato Test-psl dotato di questo
contenuto (si noti la mancanza del doppio apice di chiusura della
stringa):
2 + 3
"hello, world
PS C:\Users\ikmju> &.\Test.ps1
Nella stringa che inizia con:
In C:\Users\ikmju\Test.ps1:2 car:1
+ <<<< "hello, world
manca il carattere di terminazione: ".
In C:\Users\ikmju\Test.ps1:2 car:14
+ "hello, world <<<<
+ CategoryInfo : ParserError: (hello, world:String) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
Come si può notare, gli errori di questo tipo sono molto semplici da
correggere perché la shell li notifica immediatamente all’utente e
fornisce il dettaglio del problema.
Errori di runtime
Gli errori di runtime sono generati durante l’esecuzione degli script (e
dei cmdlet) e sono tipicamente dovuti a problemi con l’input fornito o
con l’ambiente circostante che non sono stati presi in considerazione
dal codice, involontariamente o intenzionalmente. Diversamente
dagli errori sintattici, dunque, questo tipo di errori non è rilevato
immediatamente dalla shell ma si presenta solo al verificarsi di
determinate condizioni.
Gli errori di questo tipo sono talvolta impossibili da eliminare, perché
dipendono da circostanze fuori dal controllo dello sviluppatore dello
script. Spesso, però, un adeguato controllo del codice può portare
all’eliminazione della maggior parte dei possibili errori di runtime.
Dovendo richiedere all’utente la digitazione di un valore numerico,
per esempio, si potrebbe ritenere corretto uno script come questo:
$x = [int](Read-Host -Prompt 'Inserisci un numero')
Errori logici
Questa tipologia di errori è la più dannosa e difficile da rilevare. A
differenza di quelli sintattici e di runtime, infatti, questi errori non
comportano di per sé la visualizzazione di alcun messaggio, ma
portano alla produzione di un risultato diverso da quello atteso o,
tipicamente, causano errori di runtime in blocchi differenti dello
script, cui viene fornito un input inatteso.
Nel blocco che segue, per esempio, un semplice ciclo dovrebbe
iterare una collezione di interi, mostrando a video il valore di ciascun
elemento:
$values = 1, 2, 3
Throw
Per sollevare un’eccezione in PowerShell è sufficiente utilizzare
l’istruzione throw, cui è possibile far seguire l’oggetto che rappresenta
il dettaglio dell’errore. Nel caso questo oggetto non sia presente o
non sia un’istanza di una classe di eccezione, l’istruzione provvede a
generare automaticamente un’eccezione di tipo system.Management.
Automation.RuntimeException e a impostare il valore della proprietà Message
"Prima riga"
3 / $null
"Terza riga"
Terza riga
La peculiarità principale di questo tipo di blocco è che diventa
disponibile non appena il relativo scope viene attivato. Questa
caratteristica fornisce agli utenti - tipicamente a chi ha competenze
limitate in ambito di sviluppo - una maggiore libertà di espressione
all’interno del proprio codice. Lo script esterno dell’esempio
precedente, quindi, avrebbe portato allo stesso risultato anche se il
blocco trap fosse stato definito in seguito alla riga problematica; il
blocco che segue, dunque, è analogo al precedente:
"Prima riga"
3 / $null
"Terza riga"
trap
{
Write-Host "Trap generico." }
trap
{
Write-Host "Trap generico."
}
trap [DivideByZeroException]
{
Write-Host "Trap divisione."
}
Terza riga
Infine, se in uno stesso scope fossero definiti due blocchi trap per il
medesimo tipo di eccezione, la shell prenderebbe in considerazione
solo il primo. Se lo script esterno di esempio fosse dotato di questo
corpo, dunque:
"Prima riga"
3 / $null
"Terza riga"
trap
{
Write-Host "Trap generico #1."
}
trap
{
Write-Host "Trap generico #2."
}
Terza riga
trap
{
Write-Host "Trap generico."
break
}
Try/catch/finally
Il costrutto try/catch/finally consente di gestire in maniera più organica
i blocchi di codice destinati a intervenire in caso di errori fatali. È una
funzionalità introdotta con la versione 2.0 di PowerShell e, poiché
impiega la stessa sintassi del costrutto omonimo disponibile nei
linguaggi di derivazione C++, è il metodo di gestione degli errori
utilizzato più spesso dagli utenti con precedenti esperienze di
sviluppo.
A differenza dell’istruzione trap, che si attiva automaticamente in tutto
lo scope dove è utilizzata, il costrutto try/catcn/finally obbliga l’utente a
definire esplicitamente il blocco di comandi su cui il gestore delle
eccezioni può intervenire. Opzionalmente, inoltre, è possibile definire
un blocco di codice da eseguire al termine dell’intero costrutto, a
prescindere dall’esito del blocco interno.
Per monitorare gli errori di una serie di comandi utilizzando questo
costrutto è necessario far precedere al blocco, racchiuso tra le usuali
parentesi graffe, la keyword try; il codice di gestione dell’errore, se
presente, va anch’esso racchiuso tra parentesi graffe e fatto
precedere dalla keyword catch; l’eventuale codice da eseguire al
termine del blocco, infine, va racchiuso tra parentesi graffe e fatto
seguire alla keyword finally. Sia l’istruzione catch sia l’istruzione finally
e i relativi blocchi sono opzionali ma, affinché il costrutto sia
sintatticamente corretto, è necessario che sia presente almeno una
delle due; in ogni caso, l’ordine delle keyword non può variare
rispetto a quello illustrato.
La sintassi del costrutto try/catch/finally può essere schematizzata
così:
try
{
<Comandi>
}
catch
{
<Gestione errore>
}
try
{
<Comandi>
}
finally
{
<Codice finale>
}
try
{
<Comandi>
}
catch
{
<Gestione errore>
}
finally
{
<Codice finale>
}
Nello script che segue, per esempio, si usa il costrutto try/catch per
gestire un errore fatale generico:
PS C:\Users\ikmju> try
>> {
>> "Prima riga"
>> 3 / $null
>> "Terza riga"
>> }
>> catch
>> {
>> Write-Host "Catch generico"
>> }
>>
Prima riga
Catch generico
[<Blocco try>]
catch [<Tipo eccezione1>]
{
<Gestione errore> }
catch [<Tipo eccezione2>] {
<Gestione errore> }
...
catch
{
<Gestione errore>
}
[<Blocco finally>]
Nello script che segue si impiegano tre blocchi catch distinti, in grado
di operare con tipi differenti di eccezione. Al verificarsi dell’errore
fatale, la shell utilizza il primo blocco compatibile con l’eccezione:
PS C:\Users\ikmju> try
>> {
>> "Prima riga"
>> 3 / $null
>> "Terza riga"
>> }
>> catch [IO.IOException]
>> {
>> Write-Host "Catch problemi I/O"
>> }
>> catch [DivideByZeroException]
>> {
>> Write-Host "Catch divisione"
>> }
>> catch
>> {
>> Write-Host "Catch generico"
>> }
>>
Prima riga
Catch divisione
Così come avviene all’interno del blocco trap, nel blocco catch la
variabile automatica $_ restituisce il riferimento all’errore fatale
corrente attraverso un’istanza della classe
System.Management.Automation.ErrorRecord, analizzata nel seguito del capitolo.
Diversamente dagli errori fatali, quelli non fatali non sono gestibili
attraverso i costrutti trap e try/catch/finally illustrati in precedenza.
Write-Error
Il cmdlet write-Error consente a uno script di generare errori non fatali.
L’impiego più semplice del comando consiste nello specificare una
stringa con il messaggio di errore attraverso il parametro posizionale
-Message: in tal caso la shell genera automaticamente un’eccezione di
tipo Microsoft.PowerShell.Commands.WriteErrorException. In alternativa, è
possibile fornire al comando un’eccezione, tramite il parametro -
Exception.
Conferma
Impossibile trovare il percorso 'C:\Users\ikmju\FakeFakeFake.txt' perché non esiste.
[S] Sì [T] Sì a tutti [I] Interrompi comando [O] Sospendi [?] Guida (il valore
predefinito è "S"): S
Copy-Item : Impossibile trovare il percorso 'C:\Users\ikmju\FakeFakeFake.txt' perché
non esiste.
In riga:1 car:10
+ Copy-Item <<<< DeleteMe.txt, FakeFakeFake.txt, DeleteMe2.txt C:\Temp -EA Inquire
+ CategoryInfo : ObjectNotFound:
(C:\Users\ikmju\FakeFakeFake.txt:String)
[Copy-Item], ItemNotFoundException
+ FullyQualifiedErrorId :
PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
l’eccezione:
PS C:\Users\ikmju> try
>> {
>> 3 / $null
>> }
>> catch
>> {
>> "Errore generato in: {0}" -f $_.Exception.Source
>> }
>>
Errore generato in: System-Management-Automation
NO Per una trattazione dettagliata delle eccezioni disponibili nel framework Microsoft
TA .NET si rimanda al sito Microsoft MSDN (http://msdn.microsoft.com).
Test
In riga:1 car:6
+ throw <<<< "Test"
+ CategoryInfo : OperationStopped: (Test:String) [], RuntimeException
+ FullyQualifiedErrorId : Test
$MaximumErrorCount
La variabile automatica $MaximumErrorCount è utilizzata per impostare il
numero massimo di elementi contenuti da $Error: per ragioni di
performance, infatti, vengono mantenuti in memoria solo gli ultimi
errori generati, in base a tale valore.
Per impostazione predefinita, il valore iniziale di $MaximumErrorCount è
pari a 256:
PS C:\Users\ikmju> $MaximumErrorCount
256
$LastExìtCode
La variabile $LastExitCode è automaticamente valorizzata quando si
lancia all’interno della shell un eseguibile a console; in tal caso, il
valore della variabile è pari al codice numerico di uscita ritornato
dall’eseguibile. Il lettore con precedenti esperienze con altre shell
potrebbe ritrovare nell’impiego di questa variabile alcune analogie
sia con la variabile ERRORLEVEL di COMMAND.COM e CMD nei sistemi Windows
sia, per esempio, con l’istruzione $? di bash o l’istruzione ? di ksh nei
sistemi *nix.
Nello script che segue, si richiama l’interprete dei comandi CMD,
chiedendo di lanciare il comando DIR all’interno di un drive
inesistente. In questo caso CMD ritorna un codice di uscita pari a 1, che
indica un problema.
PS C:\Users\ikmju> cmd.exe /C "DIR Z:\"
Dispositivo non pronto.
PS C:\Users\ikmju> $LASTEXITCODE
1
$?
La variabile automatica $? contiene un valore logico che varia in base
allo stato dell’ultima operazione eseguita: un valore pari a $true indica
che il comando precedente è andato a buon fine, mentre un valore
pari a $false0 segnala l’opposto.
La shell assegna il valore $false alla variabile $? appena viene rilevato
un errore - fatale o meno - da parte dello script, del cmdlet chiamato
oppure di un oggetto del framework Microsoft .NET; nel caso di
eseguibili a console, inoltre, la shell imposta automaticamente il
valore $? a $false se questi ritornano un codice di uscita numerico
diverso da 0.
Nello script che segue, per esempio, si utilizza la variabile $? per
verificare che la rimozione di un file sia andata a buon fine e, in caso
contrario, si mostra a video un messaggio:
PS C:\Users\ikmju> Remove-Item fakefakefake.txt -EA SilentlyContinue
PS C:\Users\ikmju> if (-not $?) { "Rimozione fallita!" }
Rimozione fallita!
$ErrorActionPreference
Attraverso la variabile automatica $ErrorActionPreference è possibile
modificare la gestione predefinita degli errori non fatali all’interno dei
cmdlet. Il valore contenuto in questa variabile, infatti, fornisce
automaticamente ai cmdlet la preferenza per il parametro -ErrorAction
nel caso quest’ultimo non sia esplicitamente fornito dall’utente.
Come ci si potrebbe aspettare, dunque, questa variabile può
assumere gli stessi valori del parametro correlato, riportati nella
Tabella 28.1.
Come dimostra lo script che segue, per esempio, impostando il
valore di $ErrorAc-tionPreference a Stop è possibile modificare il flusso
predefinito di gestione degli errori non fatali dei cmdlet affinché ogni
problema rilevato generi un errore fatale e termini l’esecuzione del
comando (si noti la presenza del file residuo, al termine della
chiamata a Remove-Item):
PS C:\Users\ikmju> $ErrorActionPreference = 'Stop'
PS C:\Users\ikmju>
PS C:\Users\ikmju> "Test" > "DeleteMe.txt"
PS C:\Users\ikmju> "Test" > "DeleteMe2.txt"
PS C:\Users\ikmju> Remove-Item DeleteMe.txt, FakeFakeFake.txt, DeleteMe2.txt
Remove-Item : Cannot find path 'C:\Users\ikmju\FakeFakeFake.txt' because it does not
exist.
At line:1 char:12
+ Remove-Item <<<< DeleteMe.txt, FakeFakeFake.txt, DeleteMe2.txt
+ CategoryInfo : ObjectNotFound:
(C:\Users\ikmju\FakeFakeFake.txt:String)
[Remove-Item], ItemNotFoundExce
ption
+ FullyQualifiedErrorId :
PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand