Dela via


Allt du ville veta om variabel ersättning i strängar

Det finns många sätt att använda variabler i strängar. Jag kallar detta variabelersättning och refererar till alla gånger du vill formatera en textsträng så att den innehåller värden från variabler. Detta är något som jag ofta finner mig själv förklara för nya skriptare.

Anmärkning

Den ursprungliga versionen av den här artikeln publicerades på bloggen som skrevs av @KevinMarquette. PowerShell-teamet tackar Kevin för att ha delat det här innehållet med oss. Kolla in hans blogg på PowerShellExplained.com.

Sammanfogning

Den första klassen av metoder kan kallas sammanlänkning. Det tar i princip flera strängar och sammanfogar dem. Det finns en lång historia av att använda sammanlänkning för att skapa formaterade strängar.

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

Sammanfogning fungerar ok när det bara finns några få värden att lägga till. Men detta kan bli komplicerat snabbt.

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

Det här enkla exemplet blir redan svårare att läsa.

Variabel ersättning

PowerShell har ett annat alternativ som är enklare. Du kan ange dina variabler direkt i strängarna.

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

Den typ av citattecken som du använder runt strängen gör skillnad. En dubbel citerad sträng tillåter ersättning, men en enda citerad sträng gör det inte. Det finns tillfällen då du vill ha det ena eller det andra så du har ett alternativ.

Kommandoersättning

Det blir lite knepigt när du börjar försöka få in egenskapernas värden i en sträng. Det är här många nya människor snubblar. Låt mig först visa dig vad de tycker ska fungera (och vid nominellt värde ser nästan ut som det borde).

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

Du skulle förvänta dig att få bort CreationTime från $directory, men i stället får du den här Time: C:\windows.CreationTime som ditt värde. Anledningen är att den här typen av ersättning endast ser basvariabeln. Den betraktar punkten som en del av strängen och slutar därför att tolka värdet djupare.

Det råkar vara så att det här objektet ger en sträng som ett standardvärde när det placeras i en sträng. Vissa objekt ger dig typnamnet i stället som System.Collections.Hashtable. Bara något att hålla utkik efter.

Med PowerShell kan du utföra kommandokörning i strängen med en särskild syntax. På så sätt kan vi hämta egenskaperna för dessa objekt och köra andra kommandon för att hämta ett värde.

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

Detta fungerar bra för vissa situationer men det kan bli lika galet som sammanlänkning om du bara har några variabler.

Utförande av kommandon

Du kan köra kommandon i en sträng. Även om jag har det här alternativet, gillar jag det inte. Det blir rörigt snabbt och svårt att felsöka. Antingen kör jag kommandot och sparar till en variabel eller använder en formatsträng.

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

Formatera sträng

.NET har ett sätt att formatera strängar som jag tycker är ganska lätt att arbeta med. Låt mig först visa den statiska metoden för den innan jag visar dig PowerShell-genvägen för att göra samma sak.

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

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

Det som händer här är att strängen parsas för de token som {0} och {1}. Sedan använder den talet för att välja från de angivna värdena. Om du vill upprepa ett värde på någon plats i strängen kan du återanvända värdets nummer.

Ju mer komplicerad strängen blir, desto mer värde får du ut av den här metoden.

Formatera värden som matriser

Om formatraden blir för lång kan du placera dina värden i en matris först.

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

Detta är inte splatting eftersom jag skickar hela matrisen i, men idén är liknande.

Avancerad formatering

Jag kallade avsiktligt dessa som kommer från .NET eftersom det finns många formateringsalternativ redan väl dokumenterade på den. Det finns inbyggda sätt att formatera olika datatyper.

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

Jag tänker inte gå in på dem men jag ville bara låta dig veta att detta är en mycket kraftfull formateringsmotor om du behöver det.

Sammanfoga strängar

Ibland vill du faktiskt sammanfoga en lista med värden tillsammans. Det finns en -join operator som kan göra det åt dig. Du kan även ange ett tecken som ska kopplas mellan strängarna.

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

$servers  -join ','

Om du vill -join vissa strängar utan avgränsare måste du ange en tom sträng ''. Men om det är allt du behöver finns det ett snabbare alternativ.

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

Det är värt att påpeka att du även kan -split strängar.

Join-Path

Detta förbises ofta men är en bra cmdlet för att skapa en filsökväg.

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

Det fantastiska med detta är att det hanterar bakåtsnedstreck på rätt sätt när det sätter ihop värdena. Detta är särskilt viktigt om du tar värden från användare eller konfigurationsfiler.

Detta går också bra med Split-Path och Test-Path. Jag täcker också dessa i mitt inlägg om att läsa och spara filer.

Strängar är matriser

Jag måste nämna att jag lägger till strängar här innan jag fortsätter. Kom ihåg att en sträng bara är en matris med tecken. När du lägger till flera strängar tillsammans skapas en ny matris varje gång.

Titta på det här exemplet:

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

Det ser mycket grundläggande ut, men det du inte ser är att varje gång en sträng läggs till i $message att en helt ny sträng skapas. Minnet allokeras, data kopieras och det gamla tas bort. Inte en stor sak när det bara görs några gånger, men en loop som denna skulle verkligen avslöja problemet.

StringBuilder

StringBuilder är också mycket populärt för att skapa stora strängar från många mindre strängar. Anledningen är att den bara samlar in alla strängar som du lägger till i den och bara sammanfogar dem alla i slutet när du hämtar värdet.

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

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

Återigen är detta något som jag når ut till .NET för. Jag använder det inte ofta längre men det är bra att veta att det finns där.

Avgränsa med klammerparenteser

Detta används för suffixsammanfogning i strängen. Ibland har variabeln inte en ren ordgräns.

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

Tack Redditor u/real_parbold för den.

Här är ett alternativ till den här metoden:

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

Jag använder personligen formatsträng för detta, men det är bra att veta om du ser det i verkligheten.

Hitta och ersätta token

De flesta av dessa funktioner begränsar ditt behov av att distribuera en egen lösning, men det finns tillfällen då du kan ha stora mallfiler där du vill ersätta strängar inuti.

Låt oss anta att du hämtade en mall från en fil som innehåller mycket text.

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

Du kan ha många tokens att ersätta. Tricket är att använda en mycket distinkt token som är enkel att hitta och ersätta. Jag tenderar att använda ett specialtecken i båda ändar för att hjälpa till att särskilja det.

Jag hittade nyligen ett nytt sätt att närma mig detta. Jag bestämde mig för att lämna det här avsnittet här eftersom det här är ett mönster som ofta används.

Ersätt flera tokenar

När jag har en lista över token som jag behöver ersätta, tar jag en mer allmän metod. Jag skulle placera dem i en hashtabell och iterera över dem för att göra ersättningen.

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

Dessa token kan läsas in från JSON eller CSV om det behövs.

Exekveringskontext ExpanderaSträng

Det finns ett smart sätt att definiera en ersättningssträng med enkla citattecken och expandera variablerna senare. Titta på det här exemplet:

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

Anropet till InvokeCommand.ExpandString i den aktuella körningskontext använder variablerna i det aktuella omfånget för substitution. Det viktigaste här är att $message kan definieras mycket tidigt innan variablerna ens finns.

Om vi bara utökar det lite kan vi utföra den här ersättningen om och om igen med olika värden.

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

För att fortsätta med den här idén; du kan importera en stor e-postmall från en textfil för att göra detta. Jag måste tacka Mark Kraus för detta förslag.

Vad som än fungerar bäst för dig

Jag är förtjust i metoden med formatsträng. Jag gör definitivt detta med de mer komplicerade strängarna eller om det finns flera variabler. För något som är väldigt kort kan jag använda någon av dessa.

Något mer?

Jag gjorde stora framsteg med det här. Min förhoppning är att du går iväg och lär dig något nytt.

Om du vill veta mer om de metoder och funktioner som möjliggör stränginterpolering kan du läsa följande lista för referensdokumentationen.