Alles wat u wilde weten over het vervangen van variabelen in tekenreeksen

Er zijn veel manieren om variabelen in tekenreeksen te gebruiken. Ik noem deze variabele vervanging, maar ik verwijs naar elke keer dat u een tekenreeks wilt opmaken om waarden van variabelen op te nemen. Dit is iets dat ik vaak aan nieuwe scripters uitleg.

Notitie

De oorspronkelijke versie van dit artikel verscheen op de blog geschreven door @KevinMarquette. Het PowerShell-team bedankt Kevin voor het delen van deze inhoud met ons. Bekijk zijn blog op PowerShellExplained.com.

Samenvoeging

De eerste klasse methoden kan worden aangeduid als samenvoeging. Het neemt in feite verschillende tekenreeksen en voegt ze samen. Er is een lange geschiedenis van het gebruik van samenvoeging om opgemaakte tekenreeksen te bouwen.

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

Samenvoeging werkt ok als er slechts enkele waarden zijn om toe te voegen. Maar dit kan snel ingewikkeld worden.

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

Dit eenvoudige voorbeeld wordt al moeilijker te lezen.

Variabele vervanging

PowerShell heeft een andere optie die eenvoudiger is. U kunt uw variabelen rechtstreeks in de tekenreeksen opgeven.

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

Het type aanhalingstekens dat u rond de tekenreeks gebruikt, maakt een verschil. Een tekenreeks met dubbele aanhalingstekens staat de vervanging toe, maar één aanhalingstekenreeks niet. Er zijn momenten waarop u een of de andere wilt, zodat u een optie hebt.

Vervanging van opdracht

Het wordt een beetje lastig wanneer u begint met het ophalen van de waarden van eigenschappen in een tekenreeks. Hier komen veel nieuwe mensen naar boven. Laat me eerst zien wat ze ervan vinden om te werken (en bij nominale waarde ziet het er bijna zo uit).

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

U zou verwachten dat u het uit de CreationTime$directoryweg gaat, maar in plaats daarvan krijgt u dit Time: c:\windows.CreationTime als uw waarde. De reden hiervoor is dat dit type vervanging alleen de basisvariabele ziet. De periode wordt als onderdeel van de tekenreeks beschouwd, zodat de waarde niet meer wordt omgezet.

Het is zo dat dit object een tekenreeks als een standaardwaarde geeft wanneer het in een tekenreeks wordt geplaatst. Sommige objecten geven u in plaats daarvan de typenaam, zoals System.Collections.Hashtable. Gewoon iets om op te letten.

Met PowerShell kunt u opdrachtuitvoering uitvoeren in de tekenreeks met een speciale syntaxis. Hierdoor kunnen we de eigenschappen van deze objecten ophalen en een andere opdracht uitvoeren om een waarde op te halen.

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

Dit werkt prima voor sommige situaties, maar het kan net zo gek worden als samenvoeging als u slechts een paar variabelen hebt.

Opdrachtuitvoering

U kunt opdrachten in een tekenreeks uitvoeren. Hoewel ik deze optie heb, vind ik het niet leuk. Het wordt snel en moeilijk om fouten op te sporen. Ik voer de opdracht uit en sla deze op in een variabele of gebruik een opmaaktekenreeks.

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

Notatietekenreeks

.NET heeft een manier om tekenreeksen op te maken waarmee ik vrij eenvoudig kan werken. Laat me eerst de statische methode voor het weergeven voordat ik u de PowerShell-snelkoppeling laat zien om hetzelfde te doen.

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

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

Wat er hier gebeurt, is dat de tekenreeks wordt geparseerd voor de tokens {0} en {1}vervolgens dat getal wordt gebruikt om uit de opgegeven waarden te kiezen. Als u één waarde in de tekenreeks wilt herhalen, kunt u dat waardenummer opnieuw gebruiken.

Hoe ingewikkelder de tekenreeks wordt, hoe meer waarde u uit deze benadering krijgt.

Waarden opmaken als matrices

Als de opmaakregel te lang wordt, kunt u uw waarden eerst in een matrix plaatsen.

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

Dit is niet splatting omdat ik de hele matrix doorgeeft, maar het idee is vergelijkbaar.

Geavanceerde opmaak

Ik heb deze opzettelijk genoemd als afkomstig van .NET, omdat er veel opmaakopties al goed zijn gedocumenteerd . Er zijn ingebouwde manieren om verschillende gegevenstypen op te maken.

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

Ik ga ze niet in, maar ik wilde je alleen laten weten dat dit een zeer krachtige opmaakengine is als je het nodig hebt.

Tekenreeksen samenvoegen

Soms wilt u een lijst met waarden samenvoegen. Er is een -join operator die dat voor u kan doen. U kunt zelfs een teken opgeven dat moet worden samengevoegd tussen de tekenreeksen.

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

$servers  -join ','

Als u tekenreeksen zonder scheidingsteken wilt -join gebruiken, moet u een lege tekenreeks ''opgeven. Maar als dat alles is wat u nodig hebt, is er een snellere optie.

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

Het is ook de moeite waard om aan te geven dat u ook -split tekenreeksen kunt gebruiken.

Join-Path

Dit wordt vaak over het hoofd gezien, maar een geweldige cmdlet voor het bouwen van een bestandspad.

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

Het grote ding hiervan is dat de backslashes correct worden uitgevoerd wanneer de waarden samen worden geplaatst. Dit is vooral belangrijk als u waarden van gebruikers of configuratiebestanden gebruikt.

Dit gaat ook goed met Split-Path en Test-Path. Ik bedek deze ook in mijn post over lezen en opslaan in bestanden.

Tekenreeksen zijn matrices

Ik moet hier wel tekenreeksen toevoegen voordat ik verder ga. Houd er rekening mee dat een tekenreeks slechts een matrix met tekens is. Wanneer u meerdere tekenreeksen aan elkaar toevoegt, wordt er elke keer een nieuwe matrix gemaakt.

Bekijk dit voorbeeld:

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

Het ziet er heel eenvoudig uit, maar wat u niet ziet, is dat telkens wanneer een tekenreeks wordt toegevoegd aan $message die hele nieuwe tekenreeks wordt gemaakt. Geheugen wordt toegewezen, gegevens worden gekopieerd en de oude wordt verwijderd. Niet erg als het maar een paar keer wordt gedaan, maar een lus zoals dit zou het probleem echt blootstellen.

StringBuilder

StringBuilder is ook erg populair voor het bouwen van grote tekenreeksen van veel kleinere tekenreeksen. De reden hiervoor is dat hiermee alleen alle tekenreeksen worden verzameld die u eraan toevoegt en alleen alle tekenreeksen aan het einde samenvoegt wanneer u de waarde ophaalt.

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

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

Nogmaals, dit is iets waarvoor ik naar .NET ga. Ik gebruik het niet vaak meer, maar het is goed om te weten dat het er is.

Delineatie met accolades

Dit wordt gebruikt voor achtervoegselsamenvoeging in de tekenreeks. Soms heeft uw variabele geen schone woordgrens.

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

Bedankt /u/real_parbold voor die.

Hier volgt een alternatief voor deze benadering:

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

Ik gebruik hier persoonlijk opmaaktekenreeks voor, maar dit is goed om te weten in het geval dat u het in het wild ziet.

Tokens zoeken en vervangen

Hoewel de meeste van deze functies uw eigen oplossing beperken, zijn er momenten waarop u grote sjabloonbestanden hebt waarin u tekenreeksen wilt vervangen.

Stel dat u een sjabloon hebt opgehaald uit een bestand met veel tekst.

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

Mogelijk hebt u veel tokens om te vervangen. De truc is om een zeer uniek token te gebruiken dat gemakkelijk te vinden en te vervangen is. Ik gebruik vaak een speciaal teken aan beide uiteinden om het te onderscheiden.

Ik heb onlangs een nieuwe manier gevonden om dit te benaderen. Ik heb besloten deze sectie hier achter te laten, omdat dit een patroon is dat vaak wordt gebruikt.

Meerdere tokens vervangen

Wanneer ik een lijst met tokens heb die ik moet vervangen, neem ik een algemenere benadering. Ik zou ze in een hashtabel plaatsen en ze herhalen om de vervanging uit te voeren.

$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
}

Deze tokens kunnen indien nodig worden geladen vanuit JSON of CSV.

ExecutionContext ExpandString

Er is een slimme manier om een vervangingstekenreeks te definiëren met enkele aanhalingstekens en de variabelen later uit te vouwen. Bekijk dit voorbeeld:

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

De aanroep van .InvokeCommand.ExpandString de huidige uitvoeringscontext maakt gebruik van de variabelen in het huidige bereik voor vervanging. Het belangrijkste hier is dat de $message variabelen heel vroeg kunnen worden gedefinieerd voordat de variabelen zelfs bestaan.

Als we dat een beetje uitvouwen, kunnen we deze vervanging uitvoeren over en over verschillende waarden.

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

Om dit idee verder te gaan; U kunt een grote e-mailsjabloon importeren uit een tekstbestand om dit te doen. Ik moet Mark Kraus bedanken voor deze suggestie.

Wat het beste voor u werkt

Ik ben een fan van de notatietekenreeksbenadering. Ik doe dit zeker met de gecompliceerdere tekenreeksen of als er meerdere variabelen zijn. Over alles wat erg kort is, kan ik een van deze gebruiken.

Nog iets?

Ik heb veel terrein behandeld op deze. Ik hoop dat je iets nieuws leert.