Udostępnij za pośrednictwem


Wszystko, co chciałeś wiedzieć o podstawieniu zmiennych w ciągach

Istnieje wiele sposobów używania zmiennych w ciągach. Nazywam to podstawianie zmiennej, ale odwołujem się do dowolnego momentu, gdy chcesz sformatować ciąg, aby uwzględnić wartości ze zmiennych. Często wyjaśniam to nowym skrypterom.

Uwaga

Oryginalna wersja tego artykułu pojawiła się na blogu prowadzonym przez @KevinMarquette. Zespół programu PowerShell dziękuje Kevinowi za udostępnienie tej zawartości nam. Zapoznaj się ze swoim blogiem na PowerShellExplained.com.

Łączenie

Pierwszą klasę metod można nazwać łączeniem. Zasadniczo bierze kilka ciągów i łączy je. Istnieje długa historia używania łączenia do tworzenia sformatowanych ciągów.

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

Łączenie działa ok, gdy istnieje tylko kilka wartości do dodania. Jednak to może się szybko skomplikować.

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

Ten prosty przykład jest już coraz trudniejszy do odczytania.

Podstawianie zmiennych

Program PowerShell ma inną opcję, która jest łatwiejsza. Zmienne można określić bezpośrednio w ciągach.

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

Typ cudzysłowów, których używasz wokół ciągu, ma znaczenie. Ciąg w podwójnych cudzysłowach umożliwia podstawienie, ale ciąg w pojedynczych cudzysłowach nie. Czasami potrzebujesz jednego lub drugiego, więc masz opcję.

Podstawianie poleceń

Gdy zaczniesz próbować uzyskać wartości właściwości w ciągu, sytuacja jest nieco trudna. Jest to miejsce, w którym wiele nowych ludzi potknęła się. pl-PL: Najpierw pozwól mi pokazać, co według nich powinno działać (i na pierwszy rzut oka prawie wygląda, jakby miało).

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

Oczekujesz, że CreationTime zostanie usunięty z $directory, ale zamiast tego uzyskujesz Time: C:\windows.CreationTime jako wartość. Przyczyną jest to, że ten typ podstawienia widzi tylko zmienną podstawową. Uwzględnia on okres jako część ciągu, dzięki czemu przestaje rozpoznawać wartość bardziej głębiej.

Zdarza się, że ten obiekt daje ciąg jako wartość domyślną, gdy jest umieszczony w ciągu. Niektóre obiekty dają zamiast tego nazwę typu, taką jak System.Collections.Hashtable. Po prostu coś, na co warto zwrócić uwagę.

Program PowerShell umożliwia wykonywanie poleceń wewnątrz ciągu ze specjalną składnią. Dzięki temu możemy uzyskać właściwości tych obiektów i uruchomić dowolne inne polecenie, aby uzyskać wartość.

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

To działa świetnie w niektórych sytuacjach, ale może stać się równie skomplikowane, jak łączenie, jeśli masz tylko kilka zmiennych.

Wykonywanie poleceń

Można uruchamiać polecenia wewnątrz ciągu znaków. Mimo że mam tę opcję, nie lubię tego. Szybko staje się zagracony i trudno go debugować. Uruchamiam polecenie i albo zapisuję w zmiennej, albo używam ciągu formatu.

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

Formatowanie ciągu

Platforma .NET ma sposób formatowania ciągów, z którymi można pracować dość łatwo. Najpierw pozwólcie, że pokażemy ci metodę statyczną, zanim pokażę skrót programu PowerShell, aby wykonać to samo.

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

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

Dzieje się tak, że ciąg jest analizowany pod kątem tokenów {0} i {1}, a następnie używa tej liczby do wyboru spośród podanych wartości. Jeśli chcesz powtórzyć jedną wartość w ciągu, możesz ponownie użyć tej liczby wartości.

Im bardziej skomplikowany jest ciąg, tym większą wartość możesz uzyskać z tego podejścia.

Formatowanie wartości jako tablic

Jeśli wiersz formatu jest zbyt długi, możesz najpierw umieścić wartości w tablicy.

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

To nie jest splatting, ponieważ przekazuję całą tablicę, ale pomysł jest podobny.

Zaawansowane formatowanie

Celowo nazwałem je jako pochodzące z platformy .NET, ponieważ istnieje wiele opcji formatowania już dobrze udokumentowanych . Istnieją wbudowane sposoby formatowania różnych typów danych.

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

Nie pójdę do nich, ale chciałem po prostu poinformować cię, że jest to bardzo zaawansowany aparat formatowania, jeśli tego potrzebujesz.

Łączenie ciągów

Czasami faktycznie chcesz połączyć listę wartości razem. -join Istnieje operator, który może to zrobić za Ciebie. Umożliwia nawet określenie znaku do połączenia między ciągami.

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

$servers  -join ','

Jeśli chcesz, aby -join niektóre ciągi nie zawierały separatora, musisz określić pusty ciąg ''. Ale jeśli to wszystko, czego potrzebujesz, istnieje szybsza opcja.

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

Warto również zwrócić uwagę, że można również -split ciągi.

Join-Path

Często jest to pomijane, ale cmdlet jest doskonałym narzędziem do tworzenia ścieżki pliku.

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

Najlepsze w tym jest to, że prawidłowo obsługuje ukośniki odwrotne przy łączeniu wartości. Jest to szczególnie ważne, jeśli przyjmujesz wartości z użytkowników lub plików konfiguracji.

To również dobrze pasuje do Split-Path i Test-Path. Omówiono je również w moim poście o odczytywaniu i zapisywaniu w plikach.

Ciągi są tablicami

Muszę tu wspomnieć o dodawaniu ciągów, zanim pójdę dalej. Pamiętaj, że ciąg jest tylko tablicą znaków. Podczas dodawania wielu ciągów za każdym razem tworzona jest nowa tablica.

Przyjrzyj się temu przykładowi:

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

Wygląda to na bardzo podstawowe, ale czego nie widać, to fakt, że za każdym razem, gdy ciąg zostanie dodany do $message, tworzony jest zupełnie nowy ciąg. Pamięć zostanie przydzielona, dane zostaną skopiowane, a stary zostanie odrzucony. Nie jest to wielka sprawa, gdy robi się to tylko kilka razy, ale pętla podobna do tej naprawdę uwidacznia problem.

StringBuilder

StringBuilder jest również bardzo popularny do tworzenia dużych ciągów z wielu mniejszych ciągów. Powód jest taki, że po prostu zbiera wszystkie dodane do niego ciągi i łączy je wszystkie tylko na końcu, gdy pobierasz wartość.

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

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

Ponownie, jest to coś, dla czego docieram do platformy .NET. Nie używam go już często, ale dobrze jest wiedzieć, że jest tam.

Wyznaczanie z użyciem nawiasów klamrowych

Jest to używane do łączenia sufiksów w ciągu. Czasami zmienna nie ma wyraźnej granicy między słowami.

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

Dziękuję Redditor u/real_parbold za to.

Oto alternatywa dla tego podejścia:

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

Osobiście używam w tym celu ciągu formatu, ale dobrze jest wiedzieć, jeśli widzisz go na wolności.

Znajdowanie i zastępowanie tokenów

Większość tych funkcji ogranicza konieczność wprowadzania własnego rozwiązania, ale czasami mogą znajdować się duże pliki szablonów, w których chcesz zamienić ciągi znaków.

Załóżmy, że pobrano szablon z pliku zawierającego dużo tekstu.

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

Możesz mieć wiele tokenów do zastąpienia. Sztuczka polega na użyciu bardzo odrębnego tokenu, który jest łatwy do znalezienia i zastąpienia. Mam tendencję do używania znaku specjalnego na obu końcach, aby pomóc w odróżnieniu.

Niedawno znalazłem nowy sposób na podejście do tego. Postanowiłem opuścić tę sekcję tutaj, ponieważ jest to wzorzec, który jest powszechnie używany.

Zastępowanie wielu tokenów

Jeśli mam listę tokenów, które należy zastąpić, biorę bardziej ogólne podejście. Umieściłbym je w tabeli skrótów i iterowałbym nad nimi, aby dokonać zamiany.

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

Te tokeny można w razie potrzeby załadować z pliku JSON lub CSV.

KontekstWykonania RozszerzCiąg

Jest sprytny sposób, aby zdefiniować ciąg zastępczy za pomocą apostrofów i rozszerzyć zmienne później. Przyjrzyj się temu przykładowi:

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

Wywołanie do InvokeCommand.ExpandString w bieżącym kontekście wykonywania używa zmiennych w bieżącym zakresie do podstawienia. Kluczową rzeczą jest to, że $message można zdefiniować bardzo wcześnie, zanim zmienne nawet istnieją.

Jeśli nieco to rozszerzymy, możemy wielokrotnie wykonywać to podstawianie przy użyciu różnych wartości.

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

Aby kontynuować ten pomysł; w tym celu można zaimportować duży szablon wiadomości e-mail z pliku tekstowego. Muszę podziękować Markowi Krausowi za tę sugestię.

Co działa najlepiej dla ciebie

Jestem fanem podejścia wykorzystującego ciągi formatowania. Na pewno robię to za pomocą bardziej skomplikowanych ciągów lub jeśli istnieje wiele zmiennych. W przypadku czegoś, co jest bardzo krótkie, mogę użyć dowolnego z nich.

Czy coś jeszcze?

Zrobiłem duże postępy w tej kwestii. Mam nadzieję, że wyjdziesz z nową wiedzą.

Jeśli chcesz dowiedzieć się więcej o metodach i funkcjach, które umożliwiają interpolację ciągów, zapoznaj się z poniższą listą dokumentacji referencyjnej.