Dizelerde değişken değişimi hakkında bilmek istediğiniz her şey
Dizelerde değişkenleri kullanmanın birçok yolu vardır. Ben buna değişken ikamesi diyorum ama değişkenlerden gelen değerleri içerecek şekilde bir dizeyi biçimlendirmek istediğiniz herhangi bir zamanı kastediyorum. Bu, kendimi sık sık yeni senaryo yazarlarına açıklarken bulduğum bir şey.
Not
Bu makalenin orijinal versiyonu @KevinMarquette tarafından yazılan blogda yayınlandı. PowerShell ekibi bu içeriği bizimle paylaştığı için Kevin'e teşekkür ediyor. Lütfen PowerShellExplained.com adresindeki bloguna göz atın.
birleştirme
Yöntemlerin birinci sınıfı birleştirme olarak adlandırılabilir. Temel olarak birkaç diziyi alıp bunları bir araya getirmektir. Biçimlendirilmiş dizeler oluşturmak için birleştirmeyi kullanmanın uzun bir geçmişi vardır.
$name = 'Kevin Marquette'
$message = 'Hello, ' + $name
Eklenecek yalnızca birkaç değer olduğunda birleştirme işlemi sorunsuz çalışır. Ancak bu hızla karmaşıklaşabilir.
$first = 'Kevin'
$last = 'Marquette'
$message = 'Hello, ' + $first + ' ' + $last + '.'
Bu basit örneğin okunması şimdiden zorlaşıyor.
Değişken ikame
PowerShell'in daha kolay başka bir seçeneği daha var. Değişkenlerinizi doğrudan dizelerde belirtebilirsiniz.
$message = "Hello, $first $last."
Dizenin etrafında kullandığınız tırnak işaretleri fark yaratır. Çift tırnaklı bir dize, ikameye izin verir, ancak tek tırnaklı bir dize bunu yapmaz. Birini veya diğerini istediğiniz zamanlar vardır, bu yüzden bir seçeneğiniz vardır.
Komut ikamesi
Özelliklerin değerlerini bir dizgeye almaya çalıştığınızda işler biraz zorlaşıyor. Burası birçok yeni insanın tuzağa düştüğü yer. Öncelikle size neyin işe yaraması gerektiğini düşündüklerini göstereyim (ve görünürde neredeyse olması gerektiği gibi görünüyor).
$directory = Get-Item 'c:\windows'
$message = "Time: $directory.CreationTime"
$directory'den CreationTime'ı çıkarmayı beklersiniz, ancak bunun yerine bu Time: c:\windows.CreationTime'ı alırsınız. değer. Bunun nedeni, bu tür bir ikamenin yalnızca temel değişkeni görmesidir. Dönemi dizenin bir parçası olarak kabul eder, böylece değerin daha derin çözümlenmesini durdurur.
Öyle olur ki, bu nesne bir dizeye yerleştirildiğinde varsayılan değer olarak bir dize verir. Bazı nesneler size System.Collections.Hashtable gibi tür adını verir. Sadece izlenecek bir şey.
PowerShell, özel bir sözdizimi ile dize içinde komut yürütme yapmanıza olanak tanır. Bu, bu nesnelerin özelliklerini almamıza ve bir değer elde etmek için başka herhangi bir komutu çalıştırmamıza olanak tanır.
$message = "Time: $($directory.CreationTime)"
Bu, bazı durumlarda işe yarar ancak yalnızca birkaç değişkeniniz varsa birleştirme kadar çılgınca olabilir.
Komut yürütme
Komutları bir dize içinde çalıştırabilirsiniz. Bu seçeneğe sahip olmama rağmen hoşuma gitmiyor. Hızlı bir şekilde dağılır ve hata ayıklaması zorlaşır. Ya komutu çalıştırıp bir değişkene kaydederim ya da bir biçim dizesi kullanırım.
$message = "Date: $(Get-Date)"
Dizeyi biçimlendir
.NET'in, çalışmayı oldukça kolay bulduğum dizeleri biçimlendirmenin bir yolu var. Aynı şeyi yapmak için PowerShell kısayolunu göstermeden önce size bunun statik yöntemini göstereyim.
# .NET string format string
[string]::Format('Hello, {0} {1}.',$first,$last)
# PowerShell format string
'Hello, {0} {1}.' -f $first, $last
Burada olan şey, dizenin {0} ve {1} belirteçleri için ayrıştırılması ve ardından sağlanan değerlerden seçim yapmak için bu sayıyı kullanmasıdır. Bir değeri dizenin herhangi bir yerinde tekrarlamak istiyorsanız, bu değer sayısını yeniden kullanabilirsiniz.
Dize ne kadar karmaşık olursa, bu yaklaşımdan o kadar fazla değer elde edersiniz.
Değerleri diziler olarak biçimlendirme
Biçim satırınız çok uzarsa, önce değerlerinizi bir diziye yerleştirebilirsiniz.
$values = @(
"Kevin"
"Marquette"
)
'Hello, {0} {1}.' -f $values
Bu bir sıçrama değil çünkü tüm diziyi aktarıyorum ama fikir benzer.
Gelişmiş biçimlendirme
Bunların kasıtlı olarak .NET'ten geldiğini söyledim çünkü üzerinde zaten iyi bir şekilde belgelenmiş çok sayıda biçimlendirme seçeneği var. Çeşitli veri türlerini biçimlendirmenin yerleşik yolları vardır.
"{0:yyyyMMdd}" -f (Get-Date)
"Population {0:N0}" -f 8175133
20211110
Population 8,175,133
Bunlara girmeyeceğim ama ihtiyacınız olursa bunun çok güçlü bir formatlama motoru olduğunu bilmenizi isterim.
Dizeleri birleştirme
Bazen aslında bir değerler listesini bir araya getirmek istersiniz. Bunu sizin için yapabilecek bir -join operatörü var. Hatta dizeler arasında birleştirilecek bir karakter belirtmenize bile olanak tanır.
$servers = @(
'server1'
'server2'
'server3'
)
$servers -join ','
Bazı dizeleri ayırıcı olmadan -birleştirmek istiyorsanız, boş bir '' dizesi belirtmeniz gerekir. Ancak ihtiyacınız olan tek şey buysa, daha hızlı bir seçenek var.
[string]::Concat('server1','server2','server3')
[string]::Concat($servers)
Ayrıca dizeleri de -split yapabileceğinizi belirtmekte fayda var.
Join-Path
Bu genellikle gözden kaçırılır ancak dosya yolu oluşturmak için harika bir cmdlet'tir.
$folder = 'Temp'
Join-Path -Path 'c:\windows' -ChildPath $folder
Bunun en güzel yanı, değerleri bir araya getirdiğinde ters eğik çizgileri doğru şekilde çözebilmesidir. Kullanıcılardan veya yapılandırma dosyalarından değer alıyorsanız bu özellikle önemlidir.
Bu aynı zamanda Split-Path ve Test-Path ile de uyumludur. Dosyaları okuma ve kaydetme hakkındaki yazımda bunları da ele alıyorum.
Dizeler dizilerdir
Devam etmeden önce buraya dize eklemekten bahsetmem gerekiyor. Bir dizenin yalnızca bir karakter dizisi olduğunu unutmayın. Birden fazla dizeyi bir araya eklediğinizde her seferinde yeni bir dizi oluşturulur.
Şu örneğe bakın:
$message = "Numbers: "
foreach($number in 1..10000)
{
$message += " $number"
}
Çok basit görünüyor ancak göremediğiniz şey, $message'a her bir dize eklendiğinde tamamen yeni bir dizenin oluşturulduğudur. Bellek tahsis edilir, veriler kopyalanır ve eskisi atılır. Yalnızca birkaç kez yapıldığında pek sorun değil, ancak bunun gibi bir döngü, sorunu gerçekten ortaya çıkarır.
StringBuilder
StringBuilder ayrıca birçok küçük dizeden büyük dizeler oluşturmak için de oldukça popülerdir. Bunun nedeni, yalnızca kendisine eklediğiniz tüm dizeleri toplaması ve yalnızca değeri aldığınızda hepsini en sonunda birleştirmesidir.
$stringBuilder = New-Object -TypeName "System.Text.StringBuilder"
[void]$stringBuilder.Append("Numbers: ")
foreach($number in 1..10000)
{
[void]$stringBuilder.Append(" $number")
}
$message = $stringBuilder.ToString()
Bu da yine bunun için .NET'e başvurduğum bir konu. Artık pek sık kullanmıyorum ama orada olduğunu bilmek güzel.
Parantez ile tasvir
Bu, dize içindeki son ekin birleştirilmesi için kullanılır. Bazen değişkeninizin temiz bir kelime sınırı yoktur.
$test = "Bet"
$tester = "Better"
Write-Host "$test $tester ${test}ter"
Bunun için Redditor u/real_parbold'a teşekkür ederiz.
İşte bu yaklaşımın bir alternatifi:
Write-Host "$test $tester $($test)ter"
Write-Host "{0} {1} {0}ter" -f $test, $tester
Ben şahsen bunun için format dizesini kullanıyorum, ancak vahşi doğada görmeniz durumunda bunu bilmek güzel.
Belirteçleri bulma ve değiştirme
Bu özelliklerin çoğu kendi çözümünüzü oluşturma ihtiyacınızı sınırlasa da, içindeki dizeleri değiştirmek istediğiniz büyük şablon dosyalarınız olabileceği zamanlar olabilir.
Çok fazla metin içeren bir dosyadan şablon çektiğinizi varsayalım.
$letter = Get-Content -Path TemplateLetter.txt -RAW
$letter = $letter -replace '#FULL_NAME#', 'Kevin Marquette'
Değiştirilecek çok sayıda jetonunuz olabilir. İşin püf noktası, bulunması ve değiştirilmesi kolay, çok farklı bir token kullanmaktır. Ayırt etmeye yardımcı olmak için her iki uçta da özel bir karakter kullanma eğilimindeyim.
Yakın zamanda buna yaklaşmanın yeni bir yolunu buldum. Bu çok sık kullanılan bir kalıp olduğu için bu bölümü burada bırakmaya karar verdim.
Birden çok jetonu değiştirin
Değiştirmem gereken tokenların bir listesi olduğunda daha genel bir yaklaşım benimsiyorum. Bunları bir hash tablosuna yerleştirir ve değiştirmeyi yapmak için üzerlerinde yinelerdim.
$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
}
Gerekirse bu tokenlar JSON veya CSV'den yüklenebilir.
ExecutionContext ExpandString
Tek tırnak işaretleri ile bir ikame dizesi tanımlamanın ve değişkenleri daha sonra genişletmenin akıllıca bir yolu var. Şu örneğe bakın:
$message = 'Hello, $Name!'
$name = 'Kevin Marquette'
$string = $ExecutionContext.InvokeCommand.ExpandString($message)
Geçerli yürütme bağlamındaki .InvokeCommand.ExpandString çağrısı, değiştirme için geçerli kapsamdaki değişkenleri kullanır. Buradaki en önemli şey, $message'ın değişkenler henüz mevcut olmadan çok erken bir zamanda tanımlanabilmesidir.
Bunu biraz açarsak, bu değişimi farklı değerlerle defalarca yapabiliriz.
$message = 'Hello, $Name!'
$nameList = 'Mark Kraus','Kevin Marquette','Lee Dailey'
foreach($name in $nameList){
$ExecutionContext.InvokeCommand.ExpandString($message)
}
Bu fikri sürdürmek için; Bunu yapmak için bir metin dosyasından büyük bir e-posta şablonunu içe aktarıyor olabilirsiniz. Bu öneri için Mark Kraus'a teşekkür etmeliyim.
Sizin için en iyi olanı
Ben format dizisi yaklaşımının hayranıyım. Bunu kesinlikle daha karmaşık dizelerle veya birden fazla değişken varsa yapıyorum. Çok kısa olan herhangi bir şeyde bunlardan herhangi birini kullanabilirim.
Başka bir şey?
Bu konuda çok fazla yol kat ettim. Umudum yeni bir şeyler öğrenerek ayrılmandır.
Bağlantılar
Dize enterpolasyonunu mümkün kılan yöntemler ve özellikler hakkında daha fazla bilgi edinmek isterseniz, referans belgeleri için aşağıdaki listeye bakın.
- Birleştirme, toplama operatörünü kullanır
- Değişken ve komut değiştirme alıntılama kurallarına uyar
- Biçimlendirme, biçim operatörünü kullanır
- Dizeleri birleştirmek join operatörünü kullanır ve
Join-Path'e referans verir, ancak ayrıcaJoin-Stringhakkında da bilgi alabilirsiniz. - Diziler Diziler Hakkında bölümünde belgelenmiştir
- StringBuilder, kendi belgelerine sahip bir .NET sınıfıdır
- Dizelerdeki parantezler de alıntı kuralları kapsamındadır
- Belirteç değiştirme, değiştirme operatörünü kullanır
$ExecutionContext.InvokeCommand.ExpandString()yönteminde .NET API referans belgeleri bulunur