Programming Notes
Think it, Code it, Run it    moschini.cloud  

Introduzione a PowerShell
Data pubblicazione: 27 novembre 2022

Cos'è PowerShell

PowerShell è uno strumento a riga di comando (CLI) per la configurazione e l'automazione del sistema operativo, dei suoi componenti e anche di altre tecnologie quali SQL Server, Exhange, AWS e Google Cloud.

E' multipiattaforma: è disponibile per Windows, Linux e macOS.

In questo articolo, vedremo alcuni esempi pratici di PowerShell.


Informazioni sul computer locale

Vediamo alcuni comandi attraverso i quali recuperare informazioni sul computer locale.

Iniziamo con un comando attraverso il quale si visualizzano informazioni riguardanti il BIOS:

Get-CimInstance -ClassName Win32_BIOS

L'output del comando è:

	SMBIOSBIOSVersion : 7.004
	Manufacturer      : American Megatrends Inc.
	Name              : 7.004
	SerialNumber      : Not Applicable
	Version           : ALASKA - 1072009

È possibile visualizzare informazioni sul processore attraverso la classe WMI Win32_Processor:

Get-CimInstance -ClassName Win32_Processor

L'output è:

	DeviceID Name                                     Caption                                MaxClockSpeed SocketDesignation Manufacturer
	-------- ----                                     -------                                ------------- ----------------- ------------
	CPU0     Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz Intel64 Family 6 Model 158 Stepping 10 2201          U3E1              GenuineIntel

Per ottenere informazioni sul modello del computer, si può eseguire questo comando:

Get-CimInstance -ClassName Win32_ComputerSystem

In aggiunta, si possono ottenere informazioni sul sistema operativo tramite:

Get-CimInstance -ClassName Win32_OperatingSystem

Per visualizzare lo spazio disponibile sui dischi locali:

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3"

il cui output è:

	DeviceID DriveType ProviderName VolumeName   Size          FreeSpace
	-------- --------- ------------ ----------   ----          ---------
	C:       3                                   510718373888  58673197056
	D:       3                      Volume       2000381014016 677303054336
	G:       3                      Google Drive 16106127360   14858432512

L'opzione -Filter "DriveType=3" serve per elencare solo i dati relativi agli hard disk, escludendo, ad esempio, le schede SD.

Per visualizzare l'ora locale corrente:

Get-CimInstance -ClassName Win32_LocalTime

che produce questo output:

	Day            : 12
	DayOfWeek      : 6
	Hour           : 15
	Milliseconds   :
	Minute         : 3
	Month          : 11
	Quarter        : 4
	Second         : 41
	WeekInMonth    : 2
	Year           : 2022
	PSComputerName :

Operazioni sul file system

In questa sezione vedremo come gestire, attraverso PowerShell, alcune attività legate al file system.

  • Elenco dei file e delle directory all'interno di una directory

    Get-ChildItem -Path C:\Test -Force -Recurse
    

    Il parametro -Path, seguito dal nome della directory, definisce il path del quale visualizzare il contenuto.

    Il parametro -Force permette di visualizzare anche i file nascosti e di sistema.

    Il parametro -Recurse permette di visualizzare anche i file e le directory di livello superiore.

    E' possibile filtrare l'elenco dei file da visualizzare.


    Ad esempio, per vedere solo i file con una certa estensione si può utilizzare questo comando:

    Get-ChildItem -Path C:\Test -Include *.js
    

    Se volessimo ordinare i file, ad esempio, per nome, potremmo utilizzare questo comando:

    Get-ChildItem -Path C:\Test -Include *.js | sort name
    

    In questo caso, l'ordinamento è di tipo crescente.


    Per forzare l'ordinamento decrescente, è sufficiente specificare -Descending:

    Get-ChildItem -Path C:\Test -Include *.js | sort name -Descending
    

  • Copia di file o di directory

    Per copiare un file si può utilizzare il comando:

    Copy-Item -Path C:\Test\readme.txt -Destination C:\Test\readme.bak
    

    Se il file di destinazione è già presente, si può sovrascrivere specificando -Force:

    Copy-Item -Path C:\Test\readme.txt -Destination C:\Test\readme.bak -Force
    

    Il comando per la copia di una directory prevede una sintassi analoga:

    Copy-Item -Path C:\Test\subdir -Destination C:\Test\subdir_new -Recurse
    

    E' possibile specificare quali file copiare da una cartella ad un'altra specificando, ad esempio, l'estensione nel parametro -Filter:

    Copy-Item -Filter *.txt -Path C:\Test\subdir -Destination C:\Test\subdir_new -Recurse
    

  • Creazione di file o di directory

    Per creare una nuova directory si utilizza questo comando:

    New-Item -Path C:\Test\NuovaDir -ItemType Directory
    

    In modo analogo, per creare un nuovo file si può utilizzare questo comando:

    New-Item -Path C:\Test\NuovaDir\NuovoFile.txt -ItemType File
    

    Il file creato sarà, naturalmente, vuoto.


  • Cancellazione di file o di directory

    Per cancellare una directory con tutti i file in essa contenuti, si può utilizzare questo comando:

    Remove-Item -Path C:\Test\NuovaDir
    

    Se la directory da cancellare, l'esecuzione del comando chiederà la conferma dell'operazione.

    Per evitare questa richiesta, si deve utilizzare il parametro -Recurse:

    Remove-Item -Path C:\Test\NuovaDir -Recurse
    

    Per cancellare un file, si può utilizzare questo comando:

    Remove-Item -Path C:\Test\NuovoFile.txt
    


Operazioni su Sql Server

Come accennato, attraverso PowerShell è possibile eseguire operazioni su una istanza di Sql Server.

Ad esempio, possiamo eseguire uno script SQL che crea una tabella:

$SQLServer = "<<Nome Istanza SQL Server>>"
$db = "<<Database>>"
$createtable = "CREATE TABLE TabellaTest (Id TINYINT, Descr VARCHAR(50))"
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $db -Query $createtable
Nota

Se nel comando Invoke-Sqlcmd non specifichiamo i parametri -Username e -Password, come nell'esempio, viene utilizzata l'autenticazione Windows.

Dopo aver creato la tabella, inseriamo alcuni dati:

$SQLServer = "<<Nome Istanza SQL Server>>"
$db = "<<Database>>"
$insertdata = "INSERT INTO TabellaTest VALUES (1,'Alfa'), (2,'Beta'), (3,'Gamma'), (4,'Delta'),(5,'Epsilon')"
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $db -Query $insertdata

A questo punto possiamo leggere i dati dalla tabella:

$SQLServer = "<<Nome Istanza SQL Server>>"
$db = "<<Database>>"
$selectdata = "SELECT * FROM TabellaTest"
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $db -Query $selectdata

Come è facilmente intuibile, possiamo anche eseguire comandi di UPDATE e DELETE:

$SQLServer = "<<Nome Istanza SQL Server>>"
$db = "<<Database>>"

$updatedata = "UPDATE TabellaTest SET Descr = 'Delta new' WHERE Id = 4"
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $db -Query $updatedata

$deletedata = "DELETE FROM TabellaTest WHERE Id = 5"
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $db -Query $deletedata

Esecuzione condizionale

Così come accade nei linguaggi tradizionali, anche negli script PowerShell è possibile utilizzare istruzioni condizionali, come, ad esempio, l'istruzione IF:

$condizione = $true
if ( $condizione )
{
    Write-Output "La condizione è vera"
}

oppure, nella forma che comprende anche l' else:

$condizione = $true
if ( $condizione )
{
    Write-Output "La condizione è vera"
}
else
{
    Write-Output "La condizione è false"
}

Per eseguire più confronti tra una variabile e i suoi possibili valori, si può utilizzare l'istruzione switch:

$language = 'PowerShell'
switch ( $language )
{
    'C#'
    {
        'Il linguaggio è C#'
    }
    'PowerShell'
    {
        'Il linguaggio è PowerShell'
    }
    'SQL'
    {
        'Il linguaggio è SQL'
    }
}

La condizione può essere un'espressione contenente operatori di confronto.

Ad esempio, l'operatore di uguaglianza è -eq (senza distinzione tra maiuscole e minuscole) oppure -ceq (con distinzione tra maiuscole e minuscole).

L'operatore di disuguaglianza è -ne (senza distinzione tra maiuscole e minuscole) oppure -cne (con distinzione tra maiuscole e minuscole).

Se avessimo bisogno di confrontare valori applicando condizioni con operatori di tipo "maggiore di ..." oppure "minore di ...", potremmo utilizzare:

-gt: maggiore di ... (senza distinzione tra maiuscole e minuscole)

-cgt: maggiore di ... (con distinzione tra maiuscole e minuscole)

-ge: maggiore o uguale a ... (senza distinzione tra maiuscole e minuscole)

-cge: maggiore o uguale a ... (con distinzione tra maiuscole e minuscole)

-lt: minore di ... (senza distinzione tra maiuscole e minuscole)

-clt: minore di ... (con distinzione tra maiuscole e minuscole)

-le: minore o uguale a ... (senza distinzione tra maiuscole e minuscole)

-cle: minore o uguale a ... (con distinzione tra maiuscole e minuscole)


Si hanno a disposizione anche gli operatori logici. Ad esempio:

-not oppure !: è l'operatore di negazione

-and: è l'operatore di prodotto logico

-or: è l'operatore di somma logica


Loop

Ci sono diversi tipi di loop in PowerShell.

ForEach-Object

Attraverso il comando ForEach-Object è possibile eseguire un loop su tutti gli elementi di un oggetto.

Ad esempio, per visualizzare tutti i file della directory "c:\mydir", potremmo utilizzare questo script:

$myDocuments = Get-ChildItem c:\mydir -File

$myDocuments | ForEach-Object {$_.FullName}

oppure:

$myDocuments = Get-ChildItem c:\mydir -File

ForEach-Object -InputObject $myDocuments -Process {$_.FullName}

La variabile $_ è usata per rappresentare l'oggetto corrente all'interno del loop.

Il parametro -Process specifica l'operazione da eseguire su ogni elemento dell'oggetto.

For

Il ciclo For può essere utilizzato per avanzare all'interno di un intervallo definito da un valore minimo ed uno massimo.

Ad esempio, per visualizzare le tabelline possiamo utilizzare questo script:

For ($t=1; $t -le 10; $t++) {
	"Tabellina del " + $t
	For ($i=1; $i -le 10; $i++) {
		"$t * $i = " + ($t * $i)
    }
}

Si può utilizzare il ciclo For anche per ciclare sugli elementi di un array:

$colori = @("Rosso","Blu","Verde","Giallo","Rosa","Bianco","Nero")
For ($i=0; $i -lt $colori.Length; $i++) {
   $colori[$i]
}

While, Do-While, Do-Until

Il terzo tipo di loop supportato da PowerShell è quello caratterizzato da While, Do-While, Do-Until.

In questo caso, il loop prosegue fino a quando una certa condizione risulta vera oppure falsa .

While e Do-While sono usati per eseguire un'azione quando la condizione è vera.

Do-Until ha una sintassi simile al ciclo Do-While, con la differenza che il loop viene eseguito mentre la condizione e false e termina quando la condizione risulta vera.

Ad esempio, per visualizzare i numeri interi da 1 a 1 possiamo usare questo script:

$i=1
Do {
    $i
    $i++
}
While ($i -le 10)

oppure:

$i=1
Do {
    $i
    $i++
}
Until ($i -gt 10)

oppure:

$i=1
While ($i -le 10)
{
	$i
	$i++
}

Per uscire da un loop al verificarsi di una certa condizione, si utilizza la keyword Break:

$i=1
While ($true)
{
	$i

	$i++
	if ($i -gt 10) {
		Break
	}
}

Funzioni

In PowerShell è possibile definire e richiamare due tipologie di funzioni:

  • senza parametri
  • con parametri

Per definire una funzione senza parametri, si utilizza questa sintassi:

    function nomeFunzione
    {
        codice
    }

Per richiamare la funzione, è sufficiente utilizzare il suo nome:

	nomeFunzione

Esempio: script che definisce una funzione per visualizzare la data corrente:

    function displayDate
    {
   	    Get-Date
    }

	displayDate

Per le funzioni con parametri, è possibile scegliere tra due sintassi:

  • la prima sintassi permette la definizione dei parametri per nome (named parameters) ed utilizza la la keyword param.

    Esempio:

        function dividiValori()
        {
            Param ([int]$a,[int]$b)
            $quoziente = $a / $b
            return $quoziente
        }
    

    La funzione dividiValori può essere richiamata in questo modo:

        dividiValori -a 10 -b 2
    

    oppure in questo modo:

        dividiValori -b 2 -a 10
    

    oppure senza definire i parametri ma soltato i valori separati da uno spazio:

        dividiValori 10 2
    

    ottenendo lo stesso risultato, in quanto, nei primi due casi, i parametri sono passati per nome mentre nel terzo caso il passaggio dei valori avviene per posizione, quindi con gli stessi valori delle precedenti chiamate.

  • la seconda sintassi non prevede la keyword param, ma permette la dichiarazione dei parametri subito dopo il nome della funzione:

        function moltiplicaValori([int]$x,[int]$y)
        {
            $prodotto = $x * $y
            return $prodotto
        }
    

    Anche con questa sintassi, i parametri sono passati per nome, quindi le seguenti chiamate:

        moltiplicaValori -x 3 -y 5
        moltiplicaValori -y 5 -x 3
        moltiplicaValori 3 5
    

    ritornano lo stesso risultato.

E' possibile richiamare in modo ricorsivo una funzione.

Per esempio, si potrebbe definire una funzione per il calcolo del fattoriale in questo modo:

  function fattoriale([int]$numero)
  {
      if($numero -lt 0)
      {
          $risultato = 0
      }
      elseif($numero -le 1)
      {
          $risultato = 1
      }
      else
      {
          $risultato = $numero * (fattoriale($numero - 1))
      }
      return $risultato
  }

  fattoriale 5

Gestione degli errori

Per intercettare gli errori che possono verificarsi durante l'esecuzione di uno script, abbiamo a disposizione il costrutto Try/Catch.

Esempio:

try{
   Questa istruzione non è permessa
   "Questo è permesso"
}
catch{
   Write-Host "Si è verificato un errore." -BackgroundColor DarkRed
}

Come si può facilmente intuire, la riga che contiene "Questa istruzione non è permessa" genera un errore durante l'esecuzione dello script, in quanto il comando non è chiaramente corretto.

Al rilevarsi dell'errore, l'esecuzione passa al blocco catch, all'interno del quale viene visualizzato l'avviso su sfondo rosso.

E' possibile indicare un blocco Finally (opzionale), che verrà eseguito indipendentemente dal verificarsi della situazione di errore.

try{
   Istruzione non permessa
   "Questo è permesso"
}
catch{
   Write-Host "Si e' verificato un errore." -BackgroundColor DarkRed
   Write-Host "Riga: " $Error[0].InvocationInfo.ScriptLineNumber
   Write-Host "Codice: " $Error[0].InvocationInfo.Line
   Write-Host "Rilevato in: " $Error[0].InvocationInfo.PositionMessage
}
finally{
   Write-Host "Il blocco 'finally' viene sempre eseguito." -BackgroundColor Green -ForegroundColor Black
}

Nel blocco catch abbiamo utilizzato l'oggetto $Error per visualizzare informazioni aggiuntive sulla causa dell'errore stesso.

Le proprietà dell'oggetto $Error possono essere elencate con questo comando:

$Error | Get-Member | Select Name, MemberType

L'output restituito è:

 |Name                                       |MemberType
 |----                                       |----------
 |Equals                                     |Method
 |GetHashCode                                |Method
 |GetObjectData                              |Method
 |GetType                                    |Method
 |ToString                                   |Method
 |CategoryInfo                               |Property
 |ErrorDetails                               |Property
 |Exception                                  |Property
 |FullyQualifiedErrorId                      |Property
 |InvocationInfo                             |Property
 |PipelineIterationInfo                      |Property
 |ScriptStackTrace                           |Property
 |TargetObject                               |Property
 |PSMessageDetails                           |ScriptProperty