Tutto quello che c'è da sapere sulla sostituzione di variabili nelle stringhe

Esistono diversi modi per usare variabili nelle stringhe. Qui questo processo viene chiamato sostituzione di variabili, ma ci si riferisce ai casi in cui si vuole formattare una stringa in modo da includere valori di variabili. Si tratta di informazioni utili per chi si accosta alla creazione di script per la prima volta.

Nota

La versione originale di questo articolo è apparsa sul blog scritto da @KevinMarquette. Il team di PowerShell ringrazia Kevin per averne condiviso il contenuto. È possibile visitare il suo blog all'indirizzo PowerShellExplained.com.

Concatenation

La prima classe di metodi può essere definita concatenazione. Si tratta fondamentalmente di unire tra loro una serie di stringhe. Esiste una lunga tradizione riguardo all'uso della concatenazione per creare stringhe formattate.

$name = 'Kevin Marquette'
$message = 'Hello, ' + $name

La concatenazione è un metodo appropriato quando i valori da aggiungere sono pochi. Tuttavia, può complicarsi rapidamente.

$first = 'Kevin'
$last = 'Marquette'
$message = 'Hello, ' + $first + ' ' + $last + '.'

Questo semplice esempio è già piuttosto difficile da leggere.

Sostituzione di variabili

PowerShell offre un'altra opzione più semplice. È possibile specificare le variabili direttamente nelle stringhe.

$message = "Hello, $first $last."

Il tipo di virgolette usate per racchiudere la stringa fa la differenza. Una stringa racchiusa tra virgolette doppie consente la sostituzione, una tra virgolette singole no. In alcuni casi entrambe le opzioni possono essere valide.

Sostituzione di comandi

Quando si prova a ottenere i valori delle proprietà in una stringa, la situazione diventa più complessa. Questo è il punto in cui molti si bloccano. Prima di tutto, verrà mostrato un approccio ritenuto generalmente valido e che a prima vista sembra funzionare.

$directory = Get-Item 'c:\windows'
$message = "Time: $directory.CreationTime"

Ci si aspetterebbe di ottenere CreationTime da $directory, ma viene invece restituito Time: c:\windows.CreationTime come valore. Il motivo è che questo tipo di sostituzione riconosce solo la variabile di base. Il punto viene considerato parte della variabile e quindi viene interrotta l'ulteriore risoluzione del valore.

Quello che avviene è che questo oggetto restituisce una stringa come valore predefinito se inserito in una stringa. Alcuni oggetti forniscono il nome del tipo anziché System.Collections.Hashtable. Questo è uno degli aspetti da controllare.

PowerShell consente l'esecuzione di comandi all'interno della stringa con una sintassi speciale. In questo modo, è possibile ottenere le proprietà di tali oggetti ed eseguire qualsiasi altro comando per ottenere un valore.

$message = "Time: $($directory.CreationTime)"

Si tratta di un'ottima soluzione in alcuni casi, ma può complicarsi come la concatenazione se sono presenti solo poche variabili.

Esecuzione di comandi

È possibile eseguire comandi all'interno di una stringa. Tuttavia, questa non è l'opzione migliore. Diventa subito problematica e il debug risulta difficile. È preferibile eseguire il comando e salvarlo in una variabile oppure usare una stringa di formato.

$message = "Date: $(Get-Date)"

Stringa di formato

.NET include uno strumento per formattare le stringhe piuttosto semplice da usare. Prima di mostrare la soluzione offerta da PowerShell, verrà presentato il metodo statico allo stesso scopo.

# .NET string format string
[string]::Format('Hello, {0} {1}.',$first,$last)

# PowerShell format string
'Hello, {0} {1}.' -f $first, $last

Quello che accade qui è che la stringa viene analizzata per i token {0} e {1}, quindi usa questo numero per selezionare i valori specificati. Se si vuole ripetere un valore in un punto qualsiasi della stringa, è possibile riutilizzare questo numero di valori.

Più complicata diventa la stringa, più valore si ottiene da questo approccio.

Formattare valori come matrici

Se la riga del formato diventa troppo lunga, è possibile inserire prima i valori in una matrice.

$values = @(
    "Kevin"
    "Marquette"
)
'Hello, {0} {1}.' -f $values

Non si tratta di splatting perché viene passata l'intera matrice, ma l'idea è simile.

Formattazione avanzata

Si è parlato intenzionalmente di .NET perché sono disponibili molte opzioni di formattazione già ben documentate al riguardo. Esistono modi predefiniti per formattare vari tipi di dati.

"{0:yyyyMMdd}" -f (Get-Date)
"Population {0:N0}" -f  8175133
20211110
Population 8,175,133

Questi non verranno esaminati qui, ma è bene sapere che si tratta di un motore di formattazione molto efficace, in caso sia necessario.

Unione di stringhe

In alcuni casi è necessario concatenare tra loro un elenco di valori. È disponibile un operatore -join che può eseguire questa operazione. L'operatore consente anche di specificare un carattere per l'unione tra le stringhe.

$servers = @(
    'server1'
    'server2'
    'server3'
)

$servers  -join ','

Se si vuole usare -join con alcune stringhe senza un separatore, è necessario specificare una stringa vuota ''. Se questa è l'unica esigenza, esiste un'opzione più veloce.

[string]::Concat('server1','server2','server3')
[string]::Concat($servers)

È importante notare che è anche possibile usare -split per le stringhe.

Join-Path

Benché spesso ignorato, questo è un ottimo cmdlet per la creazione di un percorso di file.

$folder = 'Temp'
Join-Path -Path 'c:\windows' -ChildPath $folder

L'aspetto più interessante è che le barre rovesciate vengono elaborate correttamente quando si inseriscono valori insieme. Ciò è particolarmente importante se i valori vengono recuperati da utenti o file di configurazione.

Questo approccio è utile anche con Split-Path e Test-Path. Questi vengono trattati nel mio post su lettura e salvataggio in un file.

Le stringhe sono matrici

Prima di procedere, è bene menzionare l'aggiunta di stringhe. Tenere presente che una stringa è semplicemente una matrice di caratteri. Quando si aggiungono più stringhe insieme, viene creata una nuova matrice ogni volta.

Osservare questo esempio:

$message = "Numbers: "
foreach($number in 1..10000)
{
    $message += " $number"
}

Sembra molto semplice, ma ciò che non si nota a prima vista è che ogni volta che si aggiunge una stringa a $message, viene creata una nuova stringa completa. La memoria viene allocata, i dati vengono copiati e quelli precedenti vengono ignorati. Non è un problema se si sceglie questo approccio raramente, ma un ciclo simile può effettivamente risultare problematico.

StringBuilder

Anche StringBuilder è molto diffuso per creare stringhe di grandi dimensioni da molte stringhe più piccole. Il motivo è che raccoglie tutte le stringhe aggiunte e le concatena tutte alla fine solo quando si recupera il valore.

$stringBuilder = New-Object -TypeName "System.Text.StringBuilder"

[void]$stringBuilder.Append("Numbers: ")
foreach($number in 1..10000)
{
    [void]$stringBuilder.Append(" $number")
}
$message = $stringBuilder.ToString()

Anche in questo caso, è bene ricordare .NET, in quanto può essere utile in alcuni casi.

Delineare elementi con parentesi graffe

Questo approccio viene usato per la concatenazione dei suffissi all'interno della stringa. A volte la variabile non ha confini di parola chiari.

$test = "Bet"
$tester = "Better"
Write-Host "$test $tester ${test}ter"

Grazie a /u/real_parbold per il suo contributo.

Di seguito viene mostrata un'alternativa a questo approccio:

Write-Host "$test $tester $($test)ter"
Write-Host "{0} {1} {0}ter" -f $test, $tester

L'autore preferisce usare la stringa di formato a questo scopo, ma è utile conoscere l'esistenza anche di questa alternativa, nel caso venga osservata altrove.

Ricerca e sostituzione di token

Benché la maggior parte di queste funzionalità limiti la necessità di implementare una soluzione personalizzata, a volte possono essere presenti file di modello di grandi dimensioni al cui interno è necessario sostituire le stringhe.

Si supponga di avere eseguito il pull di un modello da un file con molto testo.

$letter = Get-Content -Path TemplateLetter.txt -RAW
$letter = $letter -replace '#FULL_NAME#', 'Kevin Marquette'

Può essere necessario sostituire molti token. Il trucco consiste nell'usare un token molto particolare che sia facile da trovare e sostituire. È consigliabile usare un carattere speciale a entrambe le estremità per consentirne la distinzione.

Di seguito viene descritto un nuovo approccio a questo scopo, che ignora lo schema più tradizionale.

Sostituire più token

Quando è necessario sostituire un elenco di token, è possibile adottare un approccio più generico. È possibile inserirli in una tabella hash ed eseguirne l'iterazione per la sostituzione.

$tokenList = @{
    Full_Name = 'Kevin Marquette'
    Location = 'Orange County'
    State = 'CA'
}

$letter = Get-Content -Path TemplateLetter.txt -RAW
foreach( $token in $tokenList.GetEnumerator() )
{
    $pattern = '#{0}#' -f $token.key
    $letter = $letter -replace $pattern, $token.Value
}

Se necessario, i token possono essere caricati da JSON o CSV.

ExpandString ExecutionContext

Esiste un modo intelligente per definire una stringa di sostituzione con virgolette singole ed espandere le variabili in un secondo momento. Osservare questo esempio:

$message = 'Hello, $Name!'
$name = 'Kevin Marquette'
$string = $ExecutionContext.InvokeCommand.ExpandString($message)

La chiamata a .InvokeCommand.ExpandString nel contesto di esecuzione corrente usa le variabili nell'ambito corrente per la sostituzione. L'aspetto fondamentale qui è che $message può essere definito molto presto, anche prima dell'esistenza delle variabili.

Se si espande solo un po', è possibile eseguire questa sostituzione con valori diversi.

$message = 'Hello, $Name!'
$nameList = 'Mark Kraus','Kevin Marquette','Lee Dailey'
foreach($name in $nameList){
    $ExecutionContext.InvokeCommand.ExpandString($message)
}

Proseguendo su questa idea, si potrebbe importare un modello di messaggio di posta elettronica di grandi dimensioni da un file di testo a questo scopo. Questo suggerimento proviene da Mark Kraus.

Una scelta del tutto personale

In questo articolo si predilige l'uso di stringa di formato. Questo approccio è considerato efficace anche con le stringhe più complesse o in presenza di più variabili. In casi più semplici, è possibile scegliere uno degli altri approcci.

Altre informazioni

Questo argomento è stato approfondito con cura. L'augurio è che gli utenti abbiano appreso qualcosa di nuovo.

Per altre informazioni sui metodi e sulle funzionalità che rendono possibile l'interpolazione di stringhe, vedere l'elenco seguente per la documentazione di riferimento.