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. Jest to coś, co często wyjaśniam nowym skryptom.

Uwaga

Oryginalna wersja tego artykułu pojawiła się na blogu napisanym 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 określać jako łączenie. Zasadniczo przyjmuje kilka ciągów i łączy je ze sobą. 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. Ale może to być skomplikowane szybko.

$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 używany wokół ciągu ma znaczenie. Dwucytowy ciąg umożliwia podstawienie, ale pojedynczy ciąg cudzysłów 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ę. Po pierwsze pozwól mi pokazać, co myślą, że powinni pracować (i na wartość nominalną prawie wygląda jak powinno).

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

Oczekujesz, że CreationTime wyłączysz $directorywartość , ale zamiast tego uzyskasz tę Time: c:\windows.CreationTime wartość 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.

Dzieje się tak, że ten obiekt daje ciąg jako wartość domyślną w przypadku umieszczenia w ciągu. Niektóre obiekty dają zamiast tego nazwę typu, taką jak System.Collections.Hashtable. Po prostu coś do obejrzenia.

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 być tak szalone, jak łączenie, jeśli masz tylko kilka zmiennych.

Wykonywanie poleceń

Polecenia można uruchamiać wewnątrz ciągu. Mimo że mam tę opcję, nie lubię tego. Szybko i trudno go debugować. Uruchamiam polecenie i zapisujem w zmiennej lub 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 dla tokenów, a {1}następnie używa tej liczby do wybrania z podanych {0} wartości. Jeśli chcesz powtórzyć jedną wartość w ciągu, możesz ponownie użyć tej liczby wartości.

Tym bardziej skomplikowany jest ciąg, tym większa wartość, z której wychodzisz.

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ż przekazujem całą tablicę w, 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 sprzężenia 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

Jest to często pomijane, ale doskonałe polecenie cmdlet do tworzenia ścieżki pliku.

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

Wielką rzeczą w tym jest to, że działa poprawnie ukośniki odwrotne, gdy umieszcza wartości razem. Jest to szczególnie ważne, jeśli przyjmujesz wartości z użytkowników lub plików konfiguracji.

To również dobrze się dzieje z 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 jest tworzonych za każdym razem nowa tablica.

Przyjrzyj się temu przykładowi:

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

Wygląda to bardzo podstawowe, ale to, czego nie widzisz, jest to, że za każdym razem, gdy ciąg zostanie dodany do $message tego zupełnie nowego ciągu. 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. Przyczyną jest to, że po prostu zbiera wszystkie dodane do niego ciągi i łączy wszystkie z nich tylko na końcu po pobraniu wartości.

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

Delineation with braces (Delineation with braces) (Delineation with brac

Jest to używane do łączenia sufiksów w ciągu. Czasami zmienna nie ma czystej granicy słowa.

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

Dziękujemy /u/real_parbold za ten.

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ć, że 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ą istnieć duże pliki szablonów, w których chcesz zastąpić ciągi wewnątrz.

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że być 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 specjalnego charakteru na obu końcach, aby pomóc go odróżnić.

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 iteruję nad nimi, aby zastąpić.

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

ExecutionContext ExpandString

Istnieje sprytny sposób definiowania ciągu podstawienia z pojedynczymi cudzysłowami i rozszerzania zmiennych później. Przyjrzyj się temu przykładowi:

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

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

Jeśli rozszerzymy to nieco, możemy wykonać to podstawianie do i na podstawie różnych wartości.

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

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

Niezależnie od tego, co działa najlepiej dla Ciebie

Jestem fanem podejścia do ciągu formatu. Na pewno robię to za pomocą bardziej skomplikowanych ciągów lub jeśli istnieje wiele zmiennych. Na wszystko, co jest bardzo krótkie, mogę użyć jednego z nich.

Czy coś jeszcze?

Pokryte dużo ziemi na tym jednym. Mam nadzieję, że odejdziesz od nauki czegoś nowego.

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.