Wszystko, co chcesz 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 każdego momentu, gdy chcesz sformatować ciąg do uwzględnienia 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. Sprawdź swój blog na PowerShellExplained.com.

Łączenie

Pierwsza klasa metod może być nazywana łączeniem. 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 prawidłowo, gdy istnieje tylko kilka wartości do dodania. Ale może to szybko się komplikować.

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

Ten prosty przykład jest już coraz trudniej odczytać.

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. Podwójny ciąg cudzysłów umożliwia podstawienie, ale pojedynczy cytowany ciąg nie jest. Czasami potrzebujesz jednego lub drugiego, więc masz opcję.

Podstawianie poleceń

Gdy zaczniesz próbować uzyskać wartości właściwości do ciągu, sytuacja jest nieco trudna. Jest to miejsce, w którym wiele nowych ludzi się potknęło. 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, CreationTime że zejdą z $directoryobiektu , 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, aby zatrzymać rozpoznawanie wartości bardziej szczegółowej.

Dzieje się tak, że ten obiekt daje ciąg jako wartość domyślną po umieszczeniu w ciągu. Niektóre obiekty dają nazwę typu, na przykład 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 pobrać właściwości tych obiektów i uruchomić inne polecenie, aby uzyskać wartość.

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

To działa świetnie w niektórych sytuacjach, ale może to 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)"

Ciąg formatu

Platforma .NET ma sposób formatowania ciągów, z którymi mogę się dość łatwo pracować. Najpierw pokażemy ci metodę statyczną, zanim pokażę skrót programu PowerShell, aby zrobić 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 skomplikowane jest uzyskanie ciągu, tym większa wartość, którą można wyjść 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 jestplatanie, ponieważ przekazujem 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 potężny aparat formatowania, jeśli tego potrzebujesz.

Łączenie ciągów

Czasami rzeczywiście chcesz połączyć listę wartości ze sobą. -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 jest potrzebne, istnieje szybsza opcja.

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

Warto również zauważyć, ż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 w przypadku przyjmowania wartości z użytkowników lub plików konfiguracji.

To również dobrze działa 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. Po dodaniu wielu ciągów do siebie jest tworzona nowa tablica za każdym razem.

Spójrz na ten przykład:

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

Wygląda to bardzo podstawowe, ale nie widać tego, że za każdym razem, gdy zostanie dodany $message do niego cały 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 taka 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)

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

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

Dziękuję /u/ real_parbold za ten jeden.

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 do tego ciągu formatu, ale dobrze jest wiedzieć, że widać go na wolności.

Znajdowanie i zastępowanie tokenów

Chociaż większość tych funkcji ogranicza potrzebę wprowadzania własnego rozwiązania, czasami mogą istnieć duże pliki szablonów, w których chcesz zastąpić ciągi.

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 podejścia do tego. Postanowiłem opuścić tę sekcję tutaj, ponieważ jest to wzorzec, który jest często używany.

Zastępowanie wielu tokenów

Jeśli mam listę tokenów, które należy zastąpić, mam 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 formatu JSON lub CSV.

ExecutionContext ExpandString

Istnieje sprytny sposób definiowania ciągu podstawienia za pomocą pojedynczych cudzysłowów i rozszerzania zmiennych później. Przyjrzyj się temu przykładowi:

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

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

Jeśli rozszerzymy to nieco, możemy wykonać to podstawianie na i przez wih 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ę.

Wszystko, 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?

Zakryłem dużo ziemi na tym. Mam nadzieję, że odejdziesz, ucząc się czegoś nowego.