Vše, co jste chtěli vědět o nahrazení proměnných v řetězcích

Existuje mnoho způsobů, jak používat proměnné v řetězcích. Tuto metodu nazývám náhradou proměnných, ale mám na mysli jakoukoli situaci, kdy chcete naformátovat řetězec tak, aby zahrnoval hodnoty z proměnných. To je něco, co se často snažím vysvětlit novým skriptovačům.

Poznámka:

původní verze tohoto článku se objevila na blogu napsaného @KevinMarquette. Tým PowerShellu děkujeme Kevinovi za sdílení tohoto obsahu s námi. Prosím, podívejte se na jeho blog na PowerShellExplained.com.

Zřetězení

První třída metod lze nazvat zřetězením. V zásadě se jedná o spojování několika řetězců dohromady. Existuje dlouhá historie použití zřetězení k vytváření formátovaných řetězců.

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

Zřetězení funguje dobře, když je jen několik hodnot k přidání. Ale to se může rychle zkomplikovat.

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

Tento jednoduchý příklad se už obtížně čte.

Nahrazení proměnných

PowerShell má další možnost, která je jednodušší. Proměnné můžete zadat přímo v řetězcích.

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

Typ uvozovek, které použijete kolem řetězce, má dopad. Řetězec s dvojitými uvozovkami umožňuje nahrazení, ale řetězec s jednoduchými uvozovkami ne. Někdy si přejete jednu nebo druhou, abyste měli možnost.

Nahrazení příkazů

Při pokusu o získání hodnot vlastností do řetězce se věci trochu zkomplikují. Tady se dostane mnoho nových lidí. Nejdřív vám ukážu, co si myslí, že by mělo fungovat (a na první pohled skoro vypadá, že by to mělo).

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

Očekáváte, že získáte CreationTime z $directory, ale místo toho dostanete jako hodnotu tento Time: C:\windows.CreationTime. Důvodem je, že tento typ náhrady vidí pouze základní proměnnou. Považuje období za součást řetězce, takže přestane hodnotu přeložit hlouběji.

Stává se, že tento objekt poskytuje řetězec jako výchozí hodnotu, když je vložen do řetězce. Některé objekty vám místo toho dávají název typu, například System.Collections.Hashtable. Jen něco, co by stálo za to sledovat.

PowerShell umožňuje provádět provádění příkazů uvnitř řetězce se speciální syntaxí. To nám umožňuje získat vlastnosti těchto objektů a spustit jakýkoli jiný příkaz k získání hodnoty.

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

Funguje to skvěle v některých situacích, ale může se stát stejně složité jako zřetězení, pokud máte jen pár proměnných.

Provádění příkazů

Příkazy můžete spouštět uvnitř řetězce. I když mám tuto možnost, nelíbí se mi to. Rychle se stává nepřehledným a obtížně se ladí. Buď spustím příkaz a uložím ho do proměnné, nebo použijem formátovací řetězec.

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

Formát řetězce

.NET má způsob, jak formátovat řetězce, se kterými je podle mě poměrně snadné pracovat. Nejdříve vám ukážu statickou metodu, a poté zkratku v PowerShellu, abyste dosáhli stejného výsledku.

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

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

Co se tady děje, je, že se řetězec analyzuje pro tokeny {0} a {1}, pak použije toto číslo k výběru z zadaných hodnot. Pokud chcete v řetězci zopakovat jednu hodnotu, můžete tuto hodnotu znovu použít.

Čím složitější je řetězec, tím větší hodnotu z tohoto přístupu získáte.

Formátování hodnot jako polí

Pokud je čára formátu příliš dlouhá, můžete nejprve hodnoty umístit do pole.

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

To není plácání, protože předávám celé pole, ale myšlenka je podobná.

Rozšířené formátování

Záměrně jsem tyto pojmy označil jako pocházející z .NET, protože existuje mnoho možností formátování, které jsou již v jeho rámci dobře zdokumentované. Existují předdefinované způsoby formátování různých datových typů.

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

Já do nich nepůjdu, ale chtěl jsem ti dát vědět, že to je velmi výkonný formátovací modul, pokud to potřebujete.

Spojování řetězců

Někdy opravdu chcete zřetězit seznam hodnot. Existuje -join operátor, který to může udělat za vás. Umožňuje dokonce zadat znak k propojení mezi řetězci.

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

$servers  -join ','

Pokud chcete -join některé řetězce bez oddělovače, musíte zadat prázdný řetězec ''. Ale pokud je to vše, co potřebujete, existuje rychlejší možnost.

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

Stojí také za zmínku, že můžete také -split řetězce.

Join-Path

Často se to přehlíží, ale je to skvělý cmdlet pro vytvoření cesty k souboru.

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

Skvělá věc na tom je, že správně zpracovává zpětné lomítka, když spojuje hodnoty dohromady. To je zvlášť důležité, pokud přebíráte hodnoty od uživatelů nebo konfiguračních souborů.

To také jde dobře s Split-Path a Test-Path. Také se tomu věnuji v mém příspěvku o čtení a ukládání do souborů.

Řetězce jsou pole.

Musím tady zmínit přidávání řetězců, než půjdu dál. Nezapomeňte, že řetězec je jen pole znaků. Když k sobě přidáte více řetězců, vytvoří se pokaždé nové pole.

Podívejte se na tento příklad:

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

Vypadá to velmi jednoduše, ale co nevidíte, je, že pokaždé, když přidáte řetězec do $message, vytvoří se zcela nový řetězec. Paměť se přidělí, data se zkopírují a stará se zahodí. Není to velký problém, když se to dělá jen několikrát, ale smyčka jako tato by skutečně odhalila problém.

StringBuilder

StringBuilder je také velmi oblíbený pro vytváření velkých řetězců ze spousty menších řetězců. Důvod je ten, že shromažďuje všechny řetězce, které do něj přidáte, a zřetězí je všechny až při načtení hodnoty na konci.

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

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

Opět se obracím na .NET kvůli něčemu. Už ji nepoužívám, ale je dobré vědět, že je tam.

Vymezení složenými závorkami

Toto se používá pro připojení přípon uvnitř řetězce. Někdy vaše proměnná nemá jasnou hranici slova.

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

Děkuji Redditor u/real_parbold za to.

Tady je alternativní řešení tohoto přístupu:

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

Pro to osobně používám formátovací řetězec, ale to je dobré vědět v případě, že to vidíte v divokém.

Vyhledání a nahrazení tokenů

I když většina těchto funkcí omezuje vaši potřebu vložit vlastní řešení, existují chvíle, kdy můžete mít velké soubory šablon, ve kterých chcete nahradit řetězce.

Předpokládejme, že jste stáhli šablonu ze souboru, který obsahuje hodně textu.

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

Možná máte spoustu tokenů, které je potřeba nahradit. Trik je použít velmi jedinečný token, který je snadno najít a nahradit. Obvykle používám speciální znak na obou koncích, abych ho rozlišil/a.

Nedávno jsem našel nový způsob, jak se k tomu dostat. Rozhodl jsem se opustit tuto část tady, protože to je vzor, který se běžně používá.

Nahrazení více tokenů

Když mám seznam tokenů, které potřebuji nahradit, mám obecnější přístup. Umístil bych je do hashovací tabulky a iteroval přes ně, abych provedl nahrazení.

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

Tyto tokeny je možné v případě potřeby načíst z JSON nebo CSV.

ExecutionContext ExpandString

Existuje chytrý způsob, jak definovat náhradní řetězec s jednoduchými uvozovkami a rozšířit proměnné později. Podívejte se na tento příklad:

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

Volání InvokeCommand.ExpandString v aktuálním kontextu spuštění používá proměnné v aktuálním rozsahu pro dosazení. Klíčovou věcí zde je, že $message lze definovat velmi brzy předtím, než proměnné existují.

Pokud to trochu rozšíříme, můžeme tuto náhradu provádět opakovaně s různými hodnotami.

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

Abychom pokračovali v tomto nápadu, můžete importovat velkou e-mailovou šablonu z textového souboru. Musím poděkovat Marku Krausovi za tento návrh.

Cokoliv vám nejvíc vyhovuje

Jsem fanouškem přístupu formátovacího řetězce. Rozhodně to dělám s složitějšími řetězci nebo pokud existuje více proměnných. Na cokoli, co je velmi krátké, mohu použít některou z těchto.

Cokoli jiného?

Probrala jsem toho hodně. Doufám, že si odneseš něco nového.

Pokud chcete získat další informace o metodách a funkcích, které umožňují interpolaci řetězců, najdete v následujícím seznamu referenční dokumentace.

  • Zřetězení používá operátor sčítání
  • Proměnná a nahrazení příkazů se řídí pravidly uvozování
  • Formátování používá operátor formátu
  • Spojování řetězců používá operátor spojení a a odkazy Join-Path, ale také si můžete přečíst o Join-String.
  • Pole jsou dokumentována v O polích
  • StringBuilder je třída .NET s vlastní vlastní dokumentací
  • Složené závorky ve větách jsou také popsány v pravidlech uvozování
  • Nahrazení tokenu používá operátor pro nahrazení
  • Metoda $ExecutionContext.InvokeCommand.ExpandString() obsahuje referenční dokumentaci k rozhraní .NET API