Tutto quello che c'è da sapere sulla sostituzione di variabili nelle stringheEverything you wanted to know about variable substitution in strings

Esistono diversi modi per usare variabili nelle stringhe.There are many ways to use variables in strings. 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.I'm calling this variable substitution but I'm referring to any time you want to format a string to include values from variables. Si tratta di informazioni utili per chi si accosta alla creazione di script per la prima volta.This is something that I often find myself explaining to new scripters.

Nota

La versione originale di questo articolo è apparsa nel blog a cura di @KevinMarquette.The original version of this article appeared on the blog written by @KevinMarquette. Il team di PowerShell ringrazia Kevin per averne condiviso il contenuto.The PowerShell team thanks Kevin for sharing this content with us. È possibile visitare il suo blog all'indirizzo PowerShellExplained.com.Please check out his blog at PowerShellExplained.com.

ConcatenazioneConcatenation

La prima classe di metodi può essere definita concatenazione.The first class of methods can be referred to as concatenation. Si tratta fondamentalmente di unire tra loro una serie di stringhe.It's basically taking several strings and joining them together. Esiste una lunga tradizione riguardo all'uso della concatenazione per creare stringhe formattate.There's a long history of using concatenation to build formatted strings.

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

La concatenazione è un metodo appropriato quando i valori da aggiungere sono pochi.Concatenation works out OK when there are only a few values to add. Tuttavia, può complicarsi rapidamente.But this can get complicated quickly.

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

Questo semplice esempio è già piuttosto difficile da leggere.This simple example is already getting harder to read.

Sostituzione di variabiliVariable substitution

PowerShell offre un'altra opzione più semplice.PowerShell has another option that is easier. È possibile specificare le variabili direttamente nelle stringhe.You can specify your variables directly in the strings.

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

Il tipo di virgolette usate per racchiudere la stringa fa la differenza.The type of quotes you use around the string makes a difference. Una stringa racchiusa tra virgolette doppie consente la sostituzione, una tra virgolette singole no.A double quoted string allows the substitution but a single quoted string doesn't. In alcuni casi entrambe le opzioni possono essere valide.There are times you want one or the other so you have an option.

Sostituzione di comandiCommand substitution

Quando si prova a ottenere i valori delle proprietà in una stringa, la situazione diventa più complessa.Things get a little tricky when you start trying to get the values of properties into a string. Questo è il punto in cui molti si bloccano.This is where many new people get tripped up. Prima di tutto, verrà mostrato un approccio ritenuto generalmente valido e che a prima vista sembra funzionare.First let me show you what they think should work (and at face value almost looks like it should).

$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.You would be expecting to get the CreationTime off of the $directory, but instead you get this Time: c:\windows.CreationTime as your value. Il motivo è che questo tipo di sostituzione riconosce solo la variabile di base.The reason is that this type of substitution only sees the base variable. Il punto viene considerato parte della variabile e quindi viene interrotta l'ulteriore risoluzione del valore.It considers the period as part of the string so it stops resolving the value any deeper.

Quello che avviene è che questo oggetto restituisce una stringa come valore predefinito se inserito in una stringa.It just so happens that this object gives a string as a default value when placed into a string. Alcuni oggetti forniscono il nome del tipo anziché System.Collections.Hashtable.Some objects give you the type name instead like System.Collections.Hashtable. Questo è uno degli aspetti da controllare.Just something to watch for.

PowerShell consente l'esecuzione di comandi all'interno della stringa con una sintassi speciale.PowerShell allows you to do command execution inside the string with a special syntax. In questo modo, è possibile ottenere le proprietà di tali oggetti ed eseguire qualsiasi altro comando per ottenere un valore.This allows us to get the properties of these objects and run any other command to get a value.

$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.This works great for some situations but it can get just as crazy as concatenation if you have just a few variables.

Esecuzione di comandiCommand execution

È possibile eseguire comandi all'interno di una stringa.You can run commands inside a string. Tuttavia, questa non è l'opzione migliore.Even though I have this option, I don't like it. Diventa subito problematica e il debug risulta difficile.It gets cluttered quickly and hard to debug. È preferibile eseguire il comando e salvarlo in una variabile oppure usare una stringa di formato.I either run the command and save to a variable or use a format string.

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

Stringa di formatoFormat string

.NET include uno strumento per formattare le stringhe piuttosto semplice da usare..NET has a way to format strings that I find fairly easy to work with. Prima di mostrare la soluzione offerta da PowerShell, verrà presentato il metodo statico allo stesso scopo.First let me show you the static method for it before I show you the PowerShell shortcut to do the same thing.

# .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.What is happening here is that the string is parsed for the tokens {0} and {1}, then it uses that number to pick from the values provided. Se si vuole ripetere un valore in un punto qualsiasi della stringa, è possibile riutilizzare questo numero di valori.If you want to repeat one value some place in the string, you can reuse that values number.

Più complicata diventa la stringa, più valore si ottiene da questo approccio.The more complicated the string gets, the more value you get out of this approach.

Formattare valori come matriciFormat values as arrays

Se la riga del formato diventa troppo lunga, è possibile inserire prima i valori in una matrice.If your format line gets too long, you can place your values into an array first.

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

Non si tratta di splatting perché viene passata l'intera matrice, ma l'idea è simile.This is not splatting because I'm passing the whole array in, but the idea is similar.

Formattazione avanzataAdvanced formatting

Si è parlato intenzionalmente di .NET perché sono disponibili molte opzioni di formattazione già ben documentate al riguardo.I intentionally called these out as coming from .NET because there are lots of formatting options already well documented on it. Esistono strumenti predefiniti per formattare i diversi tipi di dati.There are built in ways to format various data types.

"{0:yyyyMMdd}" -f (Get-Date)
"Population {0:N0}" -f  8175133

Questi non verranno esaminati qui, ma è bene sapere che si tratta di un motore di formattazione molto efficace, in caso sia necessario.I'm not going to go into them but I just wanted to let you know that this is a very powerful formatting engine if you need it.

Unione di stringheJoining strings

In alcuni casi è necessario concatenare tra loro un elenco di valori.Sometimes you actually do want to concatenate a list of values together. È disponibile un operatore -join che può eseguire questa operazione.There's a -join operator that can do that for you. L'operatore consente anche di specificare un carattere per l'unione tra le stringhe.It even lets you specify a character to join between the strings.

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

$servers  -join ','

Se si vuole usare -join con alcune stringhe senza un separatore, è necessario specificare una stringa vuota ''.If you want to -join some strings without a separator, you need to specify an empty string ''. Se questa è l'unica esigenza, esiste un'opzione più veloce.But if that is all you need, there's a faster option.

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

È importante notare che è anche possibile usare -split per le stringhe.It's also worth pointing out that you can also -split strings too.

Join-PathJoin-Path

Benché spesso ignorato, questo è un ottimo cmdlet per la creazione di un percorso di file.This is often overlooked but a great cmdlet for building a file path.

$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.The great thing about this is it works out the backslashes correctly when it puts the values together. Ciò è particolarmente importante se i valori vengono recuperati da utenti o file di configurazione.This is especially important if you are taking values from users or config files.

Questo approccio è utile anche con Split-Path e Test-Path.This also goes well with Split-Path and Test-Path. Questi vengono trattati nel mio post su lettura e salvataggio in un file.I also cover these in my post about reading and saving to files.

Le stringhe sono matriciStrings are arrays

Prima di procedere, è bene menzionare l'aggiunta di stringhe.I do need to mention adding strings here before I go on. Tenere presente che una stringa è semplicemente una matrice di caratteri.Remember that a string is just an array of characters. Quando si aggiungono più stringhe insieme, viene creata una nuova matrice ogni volta.When you add multiple strings together, a new array is created each time.

Osservare questo esempio:Look at this example:

$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.It looks very basic but what you don't see is that each time a string is added to $message that a whole new string is created. La memoria viene allocata, i dati vengono copiati e quelli precedenti vengono ignorati.Memory gets allocated, data gets copied and the old one is discarded. Non è un problema se si sceglie questo approccio raramente, ma un ciclo simile può effettivamente risultare problematico.Not a big deal when it's only done a few times, but a loop like this would really expose the issue.

StringBuilderStringBuilder

Anche StringBuilder è molto diffuso per creare stringhe di grandi dimensioni da molte stringhe più piccole.StringBuilder is also very popular for building large strings from lots of smaller strings. Il motivo è che raccoglie tutte le stringhe aggiunte e le concatena tutte alla fine solo quando si recupera il valore.The reason why is because it just collects all the strings you add to it and only concatenates all of them at the end when you retrieve the value.

$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,Again, this is something that I'm reaching out to .NET for. in quanto può essere utile in alcuni casi.I don't use it often anymore but it's good to know it's there.

Delineare elementi con parentesi graffeDelineation with braces

Questo approccio viene usato per la concatenazione dei suffissi all'interno della stringa.This is used for suffix concatenation within the string. A volte la variabile non ha confini di parola chiari.Sometimes your variable doesn't have a clean word boundary.

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

Grazie a /u/real_parbold per il suo contributo.Thank you /u/real_parbold for that one.

Di seguito viene mostrata un'alternativa a questo approccio:Here is an alternate to this approach:

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.I personally use format string for this, but this is good to know incase you see it in the wild.

Ricerca e sostituzione di tokenFind and replace tokens

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.While most of these features limit your need to roll your own solution, there are times where you may have large template files where you want to replace strings inside.

Si supponga di avere eseguito il pull di un modello da un file con molto testo.Let us assume you pulled in a template from a file that has a lot of text.

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

Può essere necessario sostituire molti token.You may have lots of tokens to replace. Il trucco consiste nell'usare un token molto particolare che sia facile da trovare e sostituire.The trick is to use a very distinct token that is easy to find and replace. È consigliabile usare un carattere speciale a entrambe le estremità per consentirne la distinzione.I tend to use a special character at both ends to help distinguish it.

Di seguito viene descritto un nuovo approccio a questo scopo,I recently found a new way to approach this. che ignora lo schema più tradizionale.I decided to leave this section in here because this is a pattern that is commonly used.

Sostituire più tokenReplace multiple tokens

Quando è necessario sostituire un elenco di token, è possibile adottare un approccio più generico.When I have a list of tokens that I need to replace, I take a more generic approach. È possibile inserirli in una tabella hash ed eseguirne l'iterazione per la sostituzione.I would place them in a hashtable and iterate over them to do the replace.

$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.Those tokens could be loaded from JSON or CSV if needed.

ExpandString ExecutionContextExecutionContext ExpandString

Esiste un modo intelligente per definire una stringa di sostituzione con virgolette singole ed espandere le variabili in un secondo momento.There's a clever way to define a substitution string with single quotes and expand the variables later. Osservare questo esempio:Look at this example:

$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.The call to .InvokeCommand.ExpandString on the current execution context uses the variables in the current scope for substitution. L'aspetto fondamentale qui è che $message può essere definito molto presto, anche prima dell'esistenza delle variabili.The key thing here is that the $message can be defined very early before the variables even exist.

Approfondendo questo concetto, è possibile eseguire questa sostituzione più e più volte con valori diversi.If we expand on that just a little bit, we can perform this substitution over and over wih different values.

$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.To keep going on this idea; you could be importing a large email template from a text file to do this. Questo Mark Kraus proviene da Mark Kraus.I have to thank Mark Kraus for this suggestion.

Una scelta del tutto personaleWhatever works the best for you

In questo articolo si predilige l'uso di stringa di formato.I'm a fan of the format string approach. Questo approccio è considerato efficace anche con le stringhe più complesse o in presenza di più variabili.I definitely do this with the more complicated strings or if there are multiple variables. In casi più semplici, è possibile scegliere uno degli altri approcci.On anything that is very short, I may use any one of these.

Manca qualcosa?Anything else?

Questo argomento è stato approfondito con cura.I covered a lot of ground on this one. L'augurio è che gli utenti abbiano appreso qualcosa di nuovo.My hope is that you walk away learning something new.