Karma tablolarla ilgili bilmek istediğiniz her şey

Bir adım geri gidip hashtable'lar hakkında konuşmak istiyorum. Artık her zaman kullanıyorum. Dün geceki kullanıcı grubu toplantımızdan sonra birine bu konuda ders veriyordum ve onunkiyle aynı kafa karışıklığını yaşadığımı fark ettim. Hashtable'lar PowerShell'de gerçekten önemlidir, bu nedenle bunları iyi anlamak iyidir.

Uyarı

Bu makalenin özgün sürümü, @KevinMarquette tarafından yazılan blogda yer almıştır. PowerShell ekibi, bu içeriği bizimle paylaştığı için Kevin'e teşekkür ederiz. Lütfen PowerShellExplained.com'daki blogunu inceleyin.

Hashtable, bir şeyler koleksiyonu olarak

İlk olarak geleneksel tanımda bir Hashtable koleksiyonu olarak görmenizi istiyorum. Bu tanım, daha sonra daha gelişmiş şeyler için kullanıldıklarında nasıl çalıştıklarını temel bir şekilde anlamanızı sağlar. Bu anlayışın atlanması genellikle kafa karışıklığının kaynağıdır.

Dizi nedir?

Hashtable'ın ne olduğuna geçmeden önce dizilerden bahsetmem gerekiyor. Bu tartışmanın amacı doğrultusunda, dizi bir değer veya nesne listesi veya koleksiyonudur.

$array = @(1,2,3,5,7,11)

Öğelerinizi bir diziye ekledikten sonra, listeyi yinelemek için kullanabilir foreach veya dizideki tek tek öğelere erişmek için bir dizin kullanabilirsiniz.

foreach($item in $array)
{
    Write-Output $item
}

Write-Output $array[3]

Değerleri aynı şekilde bir dizin kullanarak da güncelleştirebilirsiniz.

$array[2] = 13

Dizilere henüz yüzeysel bir bakış attım ama bu, karma tablolara geçerken onları doğru bağlama yerleştirmek için bir başlangıç olmalı.

Hash tablosu nedir?

PowerShell'in hash tablolarını kullanma yöntemlerine geçmeden önce, genel anlamda hash tablolarının ne olduğu hakkında temel bir teknik açıklamayla başlayacağım.

Karma tablo, bir diziye çok benzeyen bir veri yapısıdır, ancak her değeri (nesne) bir anahtar kullanarak depolarsınız. Temel bir anahtar/değer deposu. İlk olarak boş bir karma tablo oluşturacağız.

$ageList = @{}

Bir karma tablo tanımlamak için parantezler yerine küme ayraçlarının kullanıldığına dikkat edin. Ardından aşağıdaki gibi bir anahtar kullanarak bir öğe ekleriz:

$key = 'Kevin'
$value = 36
$ageList.Add( $key, $value )

$ageList.Add( 'Alex', 9 )

Kişinin adı anahtar, yaşı ise kaydetmek istediğim değer.

Erişim için köşeli ayraçları kullanma

Değerlerinizi karma tabloya ekledikten sonra, aynı anahtarı kullanarak (dizi için sahip olacağınız gibi sayısal bir dizin kullanmak yerine) bunları geri çekebilirsiniz.

$ageList['Kevin']
$ageList['Alex']

Kevin'in yaşını öğrenmek istediğimde, buna ulaşmak için adını kullanırım. Bu yaklaşımı karma tabloya değer eklemek veya güncelleştirmek için de kullanabiliriz. Bu tıpkı yukarıdaki yöntemin kullanılması gibidir Add() .

$ageList = @{}

$key = 'Kevin'
$value = 36
$ageList[$key] = $value

$ageList['Alex'] = 9

Daha sonraki bir bölümde ele alacağım değerlere erişmek ve bunları güncelleştirmek için kullanabileceğiniz başka bir söz dizimi var. PowerShell'e başka bir dilden geliyorsanız, bu örnekler daha önce karma tabloları nasıl kullandıysanız ona uygun olmalıdır.

Değerlerle hashtable oluşturma

Şu ana kadar bu örnekler için boş bir hash tablosu (karma tablo) oluşturdum. Anahtarları ve değerleri oluştururken önceden doldurabilirsiniz.

$ageList = @{
    Kevin = 36
    Alex  = 9
}

Arama tablosu olarak

Bu tür bir karma tablo için gerçek değer, bunları arama tablosu olarak kullanabilmenizdir. Aşağıda basit bir örnek verilmiştir.

$environments = @{
    Prod = 'SrvProd05'
    QA   = 'SrvQA02'
    Dev  = 'SrvDev12'
}

$server = $environments[$env]

Bu örnekte değişken için $env bir ortam belirtirsiniz ve bu ortam doğru sunucuyu seçer. Bunun gibi bir seçim için bir switch($env){...} kullanabilirsiniz, ancak karma tablo iyi bir seçenektir.

Daha sonra kullanmak üzere arama tablosunu dinamik olarak derlediğinizde bu daha da iyi hale gelir. Bu nedenle, bir şeyin referansını çapraz şekilde kontrol etmeniz gerektiğinde bu yaklaşımı kullanmayı düşünün. PowerShell ile Where-Objectboruyu filtrelemede o kadar iyi olmasaydı bunu daha da fazla göreceğimizi düşünüyorum. Performansın önemli olduğu bir durumdaysanız bu yaklaşımın dikkate alınması gerekir.

Daha hızlı olduğunu söyleyemem ama performans önemliyse test et kuralına uyuyor.

Çoklu seçim

Genel olarak, bir hashtable'ı bir anahtar/değer çifti olarak düşünür ve burada bir anahtar sağlarsınız ve bir değer alırsınız. PowerShell, birden çok değer almak için bir dizi anahtar sağlamanıza olanak tanır.

$environments[@('QA','DEV')]
$environments[('QA','DEV')]
$environments['QA','DEV']

Bu örnekte, yukarıda belirtilen aynı arama hash tablosunu kullanıyorum ve eşleşmeleri elde etmek için üç farklı dizi türü sağlıyorum. Bu, PowerShell'de çoğu kişinin farkında olmayan gizli bir mücevherdir.

Hash tablolarını yineleme

Karma tablo bir anahtar/değer çiftleri koleksiyonu olduğundan, bir dizi veya normal bir öğe listesi için yaptığınızdan farklı bir şekilde yineleme yaparsınız.

Fark edilmesi gereken ilk şey, karma tablonuzun kanalını oluşturursanız, borunun bunu tek bir nesne gibi ele alır.

PS> $ageList | Measure-Object
count : 1

Count özelliği, kaç değer içerdiğini size bildirse de.

PS> $ageList.Count
2

İhtiyacınız olan tek şey yalnızca değerlerse özelliğini kullanarak Values bu sorunu çözebilirsiniz.

PS> $ageList.Values | Measure-Object -Average
Count   : 2
Average : 22.5

Genellikle anahtarları listelemek ve değerlere erişmek için kullanmak daha yararlıdır.

PS> $ageList.Keys | ForEach-Object{
    $message = '{0} is {1} years old!' -f $_, $ageList[$_]
    Write-Output $message
}
Kevin is 36 years old
Alex is 9 years old

İşte foreach(){...} döngüsü ile aynı örnek.

foreach($key in $ageList.Keys)
{
    $message = '{0} is {1} years old' -f $key, $ageList[$key]
    Write-Output $message
}

Her anahtar için karma tabloyu dolaşıyoruz ve ardından bu anahtarı kullanarak değere erişiyoruz. Bu, karma tablolarla koleksiyon olarak çalışırken yaygın olarak kullanılan bir desendir.

GetEnumerator()

Bu da bizi hashtable'ımız üzerinde yinelemeye GetEnumerator() götürür.

$ageList.GetEnumerator() | ForEach-Object{
    $message = '{0} is {1} years old!' -f $_.Key, $_.Value
    Write-Output $message
}

Numaralandırıcı size her anahtar/değer çiftini birer birer verir. Bu kullanım örneği için özel olarak tasarlanmıştır. Mark Kraus'a bunu hatırlattın diye teşekkür ederim.

Hatalı Numaralandırma

Önemli detaylardan biri, karma tablonun numaralandırılırken üzerinde işlem yapılamamasıdır. Temel $environments örneğimizle başlıyorsak:

$environments = @{
    Prod = 'SrvProd05'
    QA   = 'SrvQA02'
    Dev  = 'SrvDev12'
}

Ve her anahtarı aynı sunucu değerine ayarlamaya çalışmak başarısız olur.

$environments.Keys | ForEach-Object {
    $environments[$_] = 'SrvDev03'
}

An error occurred while enumerating through a collection: Collection was modified;
enumeration operation may not execute.
+ CategoryInfo          : InvalidOperation: tableEnumerator:HashtableEnumerator) [],
 RuntimeException
+ FullyQualifiedErrorId : BadEnumeration

Bu, aynı zamanda iyi gibi görünmesine rağmen de başarısız olur:

foreach($key in $environments.Keys) {
    $environments[$key] = 'SrvDev03'
}

Collection was modified; enumeration operation may not execute.
    + CategoryInfo          : OperationStopped: (:) [], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException

Bu durumun püf noktası, numaralandırmayı yapmadan önce anahtarları kopyalamaktır.

$environments.Keys.Clone() | ForEach-Object {
    $environments[$_] = 'SrvDev03'
}

Uyarı

Tek bir anahtar içeren bir karma tablo kopyalayamazsınız. PowerShell bir hata oluşturur. Bunun yerine Keys özelliğini bir diziye dönüştürür ve ardından dizi üzerinde yinelersiniz.

@($environments.Keys) | ForEach-Object {
    $environments[$_] = 'SrvDev03'
}

Özellik koleksiyonu olarak Hashtable

Şimdiye kadar karma tablomuza yerleştirdiğimiz nesne türlerinin tümü aynı nesne türündeydi. Tüm bu örneklerde yaşları kullandım ve anahtar kişinin adıydı. Bu, her nesne koleksiyonunuzun bir adı olduğunda buna bakmanın harika bir yoludur. PowerShell'de hashtable kullanmanın başka bir yaygın yolu, anahtarı özelliğin adı olan bir özellik koleksiyonu tutmaktır. Sonraki örnekte bu fikre adım atacağım.

Özellik tabanlı erişim

Özellik tabanlı erişimin kullanılması, karma tablo dinamiklerini ve bunları PowerShell'de nasıl kullanabileceğinizi değiştirir. Yukarıda verdiğimiz anahtarları özellik olarak ele alan örneğimiz burada.

$ageList = @{}
$ageList.Kevin = 35
$ageList.Alex = 9

Yukarıdaki örneklerde olduğu gibi, bu örnek de karma tablo içinde yoksa bu anahtarları ekler. Anahtarlarınızı nasıl tanımladığınıza ve değerlerinizin ne olduğuna bağlı olarak, bu biraz garip veya mükemmel bir uyumdur. Yaş listesi örneği şimdiye kadar harika çalıştı. İleriye doğru gidebilmek için yeni bir örne ihtiyacımız var.

$person = @{
    name = 'Kevin'
    age  = 36
}

Bunun gibi öznitelikler $person ekleyebilir ve bunlara erişebiliriz.

$person.city = 'Austin'
$person.state = 'TX'

Aniden bu karma tablo bir nesne gibi hissetmeye ve hareket etmeye başlar. Hala bir şeyler koleksiyonu olduğundan yukarıdaki tüm örnekler geçerli olmaya devam eder. Sadece farklı bir bakış açısıyla yaklaşıyoruz.

Anahtarlar ve değerler kontrol ediliyor

Çoğu durumda, değeri şu şekilde test edebilirsiniz:

if( $person.age ){...}

Basit ama benim için birçok hatanın kaynağı oldu çünkü mantığımdaki önemli bir ayrıntıyı gözden kaçırıyorum. Anahtarın mevcut olup olmadığını test etmek için kullanmaya başladım. Değer $false veya sıfır olduğunda, bu ifade beklenmedik bir şekilde $false dönerdi.

if( $null -ne $person.age ){...}

Bu, sıfır değerler için bu soruna geçici bir çözümdür, ancak $null ve var olmayan anahtarlar için geçerli değildir. Çoğu zaman bu ayrımı yapmanız gerekmez, ancak bunu yaparken kullanabileceğiniz yöntemler vardır.

if( $person.ContainsKey('age') ){...}

Ayrıca anahtarı bilmeden veya koleksiyonun tamamını yinelemeden bir değeri test etmeniz gereken durum için de bir örneğimiz vardır ContainsValue() .

Anahtarları kaldırma ve temizleme

yöntemiyle Remove() anahtarları kaldırabilirsiniz.

$person.Remove('age')

Onlara bir $null değeri atamak, sizi sadece $null değeri olan bir anahtarla bırakır.

Hash tablosunu temizlemenin yaygın bir yolu, onu boş bir hash tablosu olarak tanımlamaktır.

$person = @{}

Bu işe yarasa da, bunun yerine Clear() yöntemini kullanmayı deneyin.

$person.Clear()

Bu, yönteminin kullanılması kendi kendine belgeleme kodu oluşturduğu ve kodun amaçlarını çok temiz hale getirdiği örneklerden biridir.

Tüm eğlenceli şeyler

Sıralı hash tablosu

Karma tablolar varsayılan olarak sıralı değildir. Geleneksel bağlamda, değerlere erişmek için her zaman bir anahtar kullandığınızda sıra önemli değildir. Özelliklerin tanımladığınız sırada kalmasını istediğinizi fark edebilirsiniz. Neyse ki bunu anahtar sözcükle gerçekleştirmenin ordered bir yolu var.

$person = [ordered]@{
    name = 'Kevin'
    age  = 36
}

Artık anahtarları ve değerleri numaralandırdığınızda, bunlar bu sırada kalır.

Satır içi karma tablo

Bir satırda karma tablo tanımlarken anahtar/değer çiftlerini noktalı virgülle ayırabilirsiniz.

$person = @{ name = 'kevin'; age = 36; }

Bunları boruda oluşturuyorsanız bu kullanışlı olacaktır.

Ortak işlem hattı komutlarında özel ifadeler

Özel veya hesaplanmış özellikler oluşturmak için karma tablo kullanımını destekleyen birkaç cmdlet vardır. Bunu genellikle Select-Object ve Format-Table ile görürsünüz. Karma tablolarda, tam olarak genişletildiğinde şuna benzer özel bir söz dizimi vardır.

$property = @{
    Name = 'TotalSpaceGB'
    Expression = { ($_.Used + $_.Free) / 1GB }
}

Name cmdlet'inin bu sütunu etiketlediğini gösterir. , Expression yürütülen bir betik bloğudur ve burada $_ kanaldaki nesnenin değeridir. İşte bu betiğin nasıl çalıştığı:

$drives = Get-PSDrive | where Used
$drives | Select-Object -Property Name, $property

Name     TotalSpaceGB
----     ------------
C    238.472652435303

Bu değişkeni bir yerde kullandım ancak satır içi olarak kolayca tanımlanabilir. Bu arada, Name'ı n ve Expression'yi e olarak kısaltabilirsiniz.

$drives | Select-Object -Property Name, @{n='TotalSpaceGB';e={($_.Used + $_.Free) / 1GB}}

Komutları bu kadar uzun hale getirmesinden ve bunun da genellikle bahsetmeyeceğim bazı kötü davranışları teşvik etmesinden şahsen hoşlanmıyorum. Betiklerde bu yaklaşımı kullanmak yerine yeni bir karma tablo veya pscustomobject istediğim tüm alan ve özelliklerle oluşturma olasılığım daha yüksektir. Ancak, bunu yapan çok fazla kod mevcut ve bunu bilmenizi istedim. Daha sonra bir pscustomobject oluşturmaktan bahsediyorum.

Özel sıralama ifadesi

Nesnelerde sıralamak istediğiniz veriler varsa, bir koleksiyonu sıralamak kolaydır. Verileri sıralamadan önce nesneye ekleyebilir veya Sort-Object için özel bir ifade oluşturabilirsiniz.

Get-ADUser | Sort-Object -Property @{ e={ Get-TotalSales $_.Name } }

Bu örnekte kullanıcıların listesini alıyorum ve yalnızca sıralama için ek bilgi almak için bazı özel cmdlet'leri kullanıyorum.

Karma Tablo listesini sıralama

Sıralamak istediğiniz karma tablo listesine sahipseniz, Sort-Object anahtarları özellik olarak ele almaz. Özel bir sıralama ifadesi kullanarak bu sorunun etrafından dolanabiliriz.

$data = @(
    @{name='a'}
    @{name='c'}
    @{name='e'}
    @{name='f'}
    @{name='d'}
    @{name='b'}
)

$data | Sort-Object -Property @{e={$_.name}}

Cmdlet'lere karma tablo ekleme

Bu, birçok kişinin erken aşamalarda keşfetmediği karma tablolarla ilgili en sevdiğim şeylerden biridir. Burada fikir, tüm özellikleri tek bir satırdaki bir cmdlet'e sağlamak yerine bunları önce bir karma tabloya paketleyebileceğinizdir. Ardından hashtable'ı işleve özel bir şekilde verebilirsiniz. Aşağıda, normal şekilde bir DHCP kapsamı oluşturma örneği verilmiştir.

Add-DhcpServerV4Scope -Name 'TestNetwork' -StartRange '10.0.0.2' -EndRange '10.0.0.254' -SubnetMask '255.255.255.0' -Description 'Network for testlab A' -LeaseDuration (New-TimeSpan -Days 8) -Type "Both"

"Splatting kullanılmadan, tüm bu şeylerin tek bir satırda tanımlanması gerekir." Ekrandan kaydırılır veya göründüğü yere kaydırılır. Şimdi bunu, splatting tekniği kullanan bir komutla karşılaştırın.

$DHCPScope = @{
    Name          = 'TestNetwork'
    StartRange    = '10.0.0.2'
    EndRange      = '10.0.0.254'
    SubnetMask    = '255.255.255.0'
    Description   = 'Network for testlab A'
    LeaseDuration = (New-TimeSpan -Days 8)
    Type          = "Both"
}
Add-DhcpServerV4Scope @DHCPScope

@ işaretinin $ yerine kullanılması, splat işlemini çağırır.

Bu örneğin ne kadar kolay okunduğunu takdir etmek için biraz zaman ayırın. Bunlar, tüm aynı değerlerle tam olarak aynı komutlardır. İkincisini anlamak ve ileriye doğru devam etmek daha kolaydır.

Komut çok uzadığında her zaman "splatting" tekniğini kullanıyorum. Penceremin sağa kaymasına neden olacak kadar uzun metni 'çok uzun' olarak tanımlarım. Bir işlevin üç özelliğini fark edersem, bunu yayılmış bir karma tablo kullanarak yeniden yazma olasılığım yüksek.

İsteğe bağlı parametreler için 'splatting' teknikleri

Splatting'i kullanmamın en yaygın yollarından biri, betiğimde başka bir yerden gelen isteğe bağlı parametrelerle ilgilenmektir. İsteğe bağlı Get-CimInstance bağımsız değişkeni olan bir $Credential çağrıyı sarmalayan bir işlevim olduğunu varsayalım.

$CIMParams = @{
    ClassName = 'Win32_BIOS'
    ComputerName = $ComputerName
}

if($Credential)
{
    $CIMParams.Credential = $Credential
}

Get-CimInstance @CIMParams

Karma tablomu ortak parametrelerle oluşturarak başlıyorum. Sonra, varsa $Credential öğesini ekliyorum. Burada sıçrama kullandığım için Get-CimInstance çağrısını kodumda sadece bir kez bulundurmam gerekiyor. Bu tasarım deseni çok temizdir ve çok sayıda isteğe bağlı parametreyi kolayca işleyebilir.

Adil olmak gerekirse, komutlarınızı parametre değerleri için $null'e izin verecek şekilde yazabilirsiniz. Yalnızca çağırdığınız diğer komutlar üzerinde her zaman denetiminiz yoktur.

Birden çok platform

Aynı cmdlet'e birden çok karma tablo ekleyebilirsiniz. Özgün "splatting" örneğimizi yeniden ele alırsak:

$Common = @{
    SubnetMask  = '255.255.255.0'
    LeaseDuration = (New-TimeSpan -Days 8)
    Type = "Both"
}

$DHCPScope = @{
    Name        = 'TestNetwork'
    StartRange  = '10.0.0.2'
    EndRange    = '10.0.0.254'
    Description = 'Network for testlab A'
}

Add-DhcpServerv4Scope @DHCPScope @Common

Çok sayıda komutla geçirmekte olduğum ortak bir parametre kümesine sahip olduğumda bu yöntemi kullanacağım.

Splatting ile Temiz Kod Yazımı

Kodunuzu daha temiz hale getiriyorsa tek bir parametreyi yaymanızda sakınca yoktur.

$log = @{Path = '.\logfile.log'}
Add-Content "logging this command" @log

Yürütülebilir dosyaları "splatting" yöntemiyle işleme

Splatting, /param:value söz dizimini kullanan bazı yürütülebilir dosyalarda da çalışır. Robocopy.exeörneğin, bunun gibi bazı parametreleri vardır.

$robo = @{R=1;W=1;MT=8}
robocopy source destination @robo

Bunun o kadar yararlı olduğunu bilmiyorum ama ilginç buldum.

Karma tablo ekleme

Karma tablolar, iki karma tabloyu birleştirmek için ekleme işlecini destekler.

$person += @{Zip = '78701'}

Bu yalnızca iki karma tablo bir anahtarı paylaşmazsa çalışır.

İç içe hash tabloları

Karma tabloları, bir karma tablonun içinde değer olarak kullanabiliriz.

$person = @{
    name = 'Kevin'
    age  = 36
}
$person.location = @{}
$person.location.city = 'Austin'
$person.location.state = 'TX'

İki anahtar içeren temel bir hash tablosuyla başladım. "location adlı bir anahtarı boş bir karma tablo ile ekledim." Sonra bu hash tabloya son iki öğeyi location ekledim. Tüm bunları satır içinde de yapabiliriz.

$person = @{
    name = 'Kevin'
    age  = 36
    location = @{
        city  = 'Austin'
        state = 'TX'
    }
}

Bu, yukarıda gördüğümüz hash tablosunun aynısını oluşturur ve özelliklere erişimi aynı şekilde sağlar.

$person.location.city
Austin

Nesnelerinizin yapısına yaklaşmanın birçok yolu vardır. İç içe geçmiş bir karma tabloya bakmanın ikinci yolu budur.

$people = @{
    Kevin = @{
        age  = 36
        city = 'Austin'
    }
    Alex = @{
        age  = 9
        city = 'Austin'
    }
}

Bu, hash tablolarını bir nesne koleksiyonu ve bir özellik koleksiyonu olarak birleştirme kavramını karıştırır. Tercih ettiğiniz yaklaşımı kullanarak iç içe yerleştirilmiş olsalar bile değerlere kolayca erişebilirsiniz.

PS> $people.kevin.age
36
PS> $people.kevin['city']
Austin
PS> $people['Alex'].age
9
PS> $people['Alex']['City']
Austin

Nokta özelliğini onu bir özellik gibi değerlendirdiğimde kullanma eğilimindeyim. Bunlar genellikle kodumda statik olarak tanımladığım şeylerdir ve bunları kafamın en üstünden tanıyorum. Listede gezinmem veya anahtarlara program aracılığıyla erişmem gerekirse anahtar adını sağlamak için köşeli ayraçları kullanıyorum.

foreach($name in $people.Keys)
{
    $person = $people[$name]
    '{0}, age {1}, is in {2}' -f $name, $person.age, $person.city
}

Karma tablo iç içe yerleştirme özelliğine sahip olmak size birçok esneklik ve seçenek sunar.

İç içe geçmiş hash tabloları inceleme

Hashtable'ları iç içe yerleştirmeye başlar başlamaz, bunları konsoldan görmenin kolay bir yoluna ihtiyacınız olacak. Son hashtable'ı alırsam şuna benzer bir çıkış elde ederim ve yalnızca bu kadar derine gider:

PS> $people
Name                           Value
----                           -----
Kevin                          {age, city}
Alex                           {age, city}

Bu tür şeyleri incelemek için ideal seçeneğim ConvertTo-Json çünkü çok net ve başka şeylerle sık sık JSON kullanıyorum.

PS> $people | ConvertTo-Json
{
    "Kevin":  {
                "age":  36,
                "city":  "Austin"
            },
    "Alex":  {
                "age":  9,
                "city":  "Austin"
            }
}

JSON'ı bilmeseniz bile aradığınızı görebilmeniz gerekir. Bunun gibi yapılandırılmış veriler için bir Format-Custom komut vardır ancak JSON görünümünü yine de daha çok beğeniyorum.

Nesne oluşturma

Bazen yalnızca bir nesneye sahip olmanız gerekir ve özellikleri tutmak için karma tablo kullanmak işi halletmez. En yaygın olarak anahtarları sütun adları olarak görmek istersiniz. A pscustomobject bunu kolaylaştırır.

$person = [pscustomobject]@{
    name = 'Kevin'
    age  = 36
}

$person

name  age
----  ---
Kevin  36

Başlangıçta bir olarak pscustomobject oluşturmasanız bile, gerektiğinde daha sonra istediğiniz zaman yayınlayabilirsiniz.

$person = @{
    name = 'Kevin'
    age  = 36
}

[pscustomobject]$person

name  age
----  ---
Kevin  36

Bu yazının ardından okumanız gereken pscustomobject hakkında zaten ayrıntılı bir yazı yazdım. Burada öğrenilen birçok şey üzerine inşa eder.

Dosyaya karma tablo okuma ve yazma

CSV'ye kaydetme

CSV dosyasına kaydetmek için bir karma tabloyu kullanmakta zorlanmak, yukarıda bahsettiğim zorluklardan biridir. Karma tablosunu bir pscustomobject olarak dönüştürdüğünüzde, doğru şekilde CSV'ye kaydedilecektir. Bir pscustomobject ile başlamak, sütun sırasının korunmasına yardımcı olur. Ancak gerekirse pscustomobject satır içi olarak dönüştürebilirsiniz.

$person | ForEach-Object{ [pscustomobject]$_ } | Export-Csv -Path $path

Bir kez daha pscustomobject kullanma ile ilgili yazmama göz atın.

Bir iç içe hashtable'ı dosyaya kaydetme

bir dosyaya iç içe karma tablo kaydetmem ve yeniden okumam gerekirse, bunu yapmak için JSON cmdlet'lerini kullanıyorum.

$people | ConvertTo-Json | Set-Content -Path $path
$people = Get-Content -Path $path -Raw | ConvertFrom-Json

Bu yöntemle ilgili iki önemli nokta vardır. İlk olarak, JSON çok satırlı olarak yazılmıştır, bu yüzden metni tek bir satırda yeniden okumak için -Raw seçeneğini kullanmam gerekir. İkincisi, içeri aktarılan nesnenin artık bir [hashtable]olmamasıdır. Artık bir [pscustomobject] ve bu, beklemediğiniz takdirde sorunlara neden olabilir.

Derinden iç içe geçmiş hash tablolara dikkat edin. JSON'a dönüştürdüğünüzde beklediğiniz sonuçları alamayabilirsiniz.

@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json

{
  "a": {
    "b": {
      "c": "System.Collections.Hashtable"
    }
  }
}

Tüm iç içe geçmiş karma tablolarını genişlettiğinizden emin olmak için Depth parametresini kullanın.

@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json -Depth 3

{
  "a": {
    "b": {
      "c": {
        "d": "e"
      }
    }
  }
}

İçeri aktarmada bir [hashtable] olması gerekiyorsa, Export-CliXml ve Import-CliXml komutlarını kullanmanız gerekir.

JSON'ı Karma Tabloya Dönüştürme

JSON'u ' [hashtable]a dönüştürmeniz gerekiyorsa, bunu .NET'teki JavaScriptSerializer ile yapmak için bildiğim bir yol var.

[Reflection.Assembly]::LoadWithPartialName("System.Web.Script.Serialization")
$JSSerializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
$JSSerializer.Deserialize($json,'Hashtable')

PowerShell v6 sürümünden itibaren JSON desteği NewtonSoft JSON.NET kullanır ve karma tablo desteği ekler.

'{ "a": "b" }' | ConvertFrom-Json -AsHashtable

Name      Value
----      -----
a         b

PowerShell 6.2, derinlik parametresini öğesine ConvertFrom-Jsonekledi. Varsayılan Derinlik 1024'dür.

Doğrudan bir dosyadan okuma

PowerShell söz dizimini kullanarak karma tablo içeren bir dosyanız varsa, bunu doğrudan aktarmanın bir yolu vardır.

$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create( $content )
$scriptBlock.CheckRestrictedLanguage( $allowedCommands, $allowedVariables, $true )
$hashtable = ( & $scriptBlock )

Dosyanın içeriğini içine scriptblockaktarır, ardından yürütmeden önce içinde başka PowerShell komutu olmadığından emin olmak için denetler.

Bu bağlamda, modül bildirim dosyasının (.psd1) yalnızca bir karma tablo olduğunu biliyor muydunuz?

Anahtarlar herhangi bir nesne olabilir

Çoğu zaman anahtarlar yalnızca karakter dizileridir. Böylece her şeyi tırnak içine alıp anahtar haline getirebiliyoruz.

$person = @{
    'full name' = 'Kevin Marquette'
    '#' = 3978
}
$person['full name']

Yapabileceğinizi fark etmeyebileceğiniz bazı garip şeyler yapabilirsiniz.

$person.'full name'

$key = 'full name'
$person.$key

Bir şeyi yapabiliyor olman, onu yapman gerektiği anlamına gelmez. Bu sonuncusu, gerçekleşmeyi bekleyen bir hata gibi görünür ve kodunuzu okuyan herkes tarafından kolayca yanlış anlaşılır.

Teknik olarak anahtarınızın bir dize olması gerekmez, ancak yalnızca dizeleri kullanıyorsanız bunları düşünmek daha kolaydır. Ancak, dizin oluşturma karmaşık anahtarlarla iyi çalışmaz.

$ht = @{ @(1,2,3) = "a" }
$ht

Name                           Value
----                           -----
{1, 2, 3}                      a

Karma tablodaki bir değere anahtarıyla erişmek her zaman işe yaramaz. Örneğin:

$key = $ht.Keys[0]
$ht.$($key)
a
$ht[$key]
a

Anahtar bir dizi olduğunda, $key değişkenini üye erişim operatörü (.) ile kullanılabilmesi için bir alt ifadeye sarmalamanız gerekir. İsterseniz dizi dizini ([]) gösterimini de kullanabilirsiniz.

Otomatik değişkenlerde kullanma

$PSBoundParameters

$PSBoundParameters yalnızca bir işlevin bağlamında var olan bir otomatik değişkendir. İşlevin çağrıldığı tüm parametreleri içerir. Bu tam olarak bir karma tablo değildir, ancak bir karma tablo gibi kullanabileceğiniz kadar benzer.

Buna anahtarların kaldırılması ve diğer işlevlere sıçraması dahildir. Ara sunucu işlevleri yazdığınızı fark ederseniz, buna daha yakından bakın.

Diğer ayrıntılar için bkz. about_Automatic_Variables .

PSBoundParameters tuzağı

Unutmamanız gereken önemli bir şey, bunun yalnızca parametre olarak geçirilen değerleri içerdiğidir. Varsayılan değerlere sahip parametreleriniz de varsa ancak çağıran tarafından geçirilmediyse, $PSBoundParameters bu değerleri içermez. Bu genellikle göz ardı edilir.

$PSDefaultParameterValues

Bu otomatik değişken, cmdlet'ini değiştirmeden herhangi bir cmdlet'e varsayılan değerler atamanızı sağlar. Bu örneğe göz atın.

$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"

Bu, $PSDefaultParameterValues parametresinin varsayılan değeri olarak belirlendiği UTF8 karma tablosuna bir Out-File -Encoding girdisi ekler. Bu oturuma özgüdür, bu nedenle $PROFILE öğesine yerleştirmeniz gerekir.

Bunu sık sık yazdığım değerleri önceden atamak için kullanıyorum.

$PSDefaultParameterValues[ "Connect-VIServer:Server" ] = 'VCENTER01.contoso.local'

Bu, değerleri toplu olarak ayarlayabilmeniz için joker karakterleri de kabul eder. Bunu kullanmanın bazı yolları şunlardır:

$PSDefaultParameterValues[ "Get-*:Verbose" ] = $true
$PSDefaultParameterValues[ "*:Credential" ] = Get-Credential

Daha ayrıntılı bir döküm için Michael Sorens'ınOtomatik Varsayılanlar hakkındaki bu harika makalesine bakın.

Regex $Eşleşmeler

işlecini -match kullandığınızda, eşleşmenin sonuçlarıyla birlikte adlı $Matches bir otomatik değişken oluşturulur. Reex'inizde herhangi bir alt ifade varsa, bu alt eşleşmeler de listelenir.

$message = 'My SSN is 123-45-6789.'

$message -match 'My SSN is (.+)\.'
$Matches[0]
$Matches[1]

Adlandırılmış eşleşmeler

Bu, çoğu kişinin bilmediği en sevdiğim özelliklerden biridir. Adlandırılmış bir regex eşleşmesi kullanıyorsanız, bu eşleşmeye eşleşmelerde ada göre erişebilirsiniz.

$message = 'My Name is Kevin and my SSN is 123-45-6789.'

if($message -match 'My Name is (?<Name>.+) and my SSN is (?<SSN>.+)\.')
{
    $Matches.Name
    $Matches.SSN
}

Yukarıdaki örnekte, (?<Name>.*) adlandırılmış bir alt ifadedir. Bu değer daha sonra $Matches.Name özelliğine yerleştirilir.

Group-Object -AsHashtable

Bilinen küçük bir özelliği Group-Object , bazı veri kümelerini sizin için bir karma tabloya dönüştürebileceğidir.

Import-Csv $Path | Group-Object -AsHashtable -Property Email

Bu işlem her satırı bir karma tabloya ekler ve erişim anahtarı olarak belirtilen özelliği kullanır.

Karma Tablo Kopyalama

Bilinmesi gereken önemli bir şey, karma tablolarının aslında nesneler olduğudur. Her değişken yalnızca bir nesneye başvurudur. Bu, şunu gösterir ki, karma tabloya ait geçerli bir kopya oluşturmak daha fazla çalışma gerektirir.

Referans türleri atama

Bir karma tablonuz olduğunda ve bunu ikinci bir değişkene atadığınızda, her iki değişken de aynı karma tabloya işaret eder.

PS> $orig = @{name='orig'}
PS> $copy = $orig
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name

Copy: [copy]
Orig: [copy]

Bu, birindeki değerlerin değiştirilmesi diğerindeki değerleri de değiştireceği için bunların aynı olduğunu vurgular. Bu, hash tablolarını diğer işlevlere geçirirken de geçerlidir. Bu işlevler bu karma tabloda değişiklik yaparsa, orijinal karma tablonuz da değiştirilir.

Sığ kopyalar, tek düzey

Yukarıdaki örneğimiz gibi basit bir karma tablomuz varsa, yüzeysel bir kopya oluşturmak için Clone() kullanabiliriz.

PS> $orig = @{name='orig'}
PS> $copy = $orig.Clone()
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name

Copy: [copy]
Orig: [orig]

Bu, birinde diğerini etkilemeyen bazı temel değişiklikler yapmamıza olanak sağlar.

İç içe yerleştirilmiş sığ kopyalar

Basit kopya olarak adlandırılıyor olmasının nedeni, yalnızca temel düzey özelliklerini kopyalamasıdır. Bu özelliklerden biri bir başvuru türüyse (başka bir karma tablo gibi), iç içe nesneler yine de birbirini gösterir.

PS> $orig = @{
        person=@{
            name='orig'
        }
    }
PS> $copy = $orig.Clone()
PS> $copy.person.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.person.name
PS> 'Orig: [{0}]' -f $orig.person.name

Copy: [copy]
Orig: [copy]

Yani karma tabloyu kopyalamış olsam da, person referansının kopyalanmadığını görebilirsiniz. İlkine bağlı olmayan ikinci bir karma tabloya gerçekten sahip olmak için derin bir kopya yapmamız gerekir.

Derin kopyalar

Bir karma tablonun derin bir kopyasını oluşturmanın ve onu karma tablo olarak tutmanın birkaç yolu vardır. Aşağıda, powershell kullanarak özyinelemeli olarak derin bir kopya oluşturan bir işlev yer alır:

function Get-DeepClone
{
    [CmdletBinding()]
    param(
        $InputObject
    )
    process
    {
        if($InputObject -is [hashtable]) {
            $clone = @{}
            foreach($key in $InputObject.Keys)
            {
                $clone[$key] = Get-DeepClone $InputObject[$key]
            }
            return $clone
        } else {
            return $InputObject
        }
    }
}

Diğer referans türlerini veya dizileri işlemez, ancak iyi bir başlangıç noktasıdır.

Bir diğer yol da bu işlevdeki gibi CliXml kullanarak seri durumdan çıkarmak için .NET kullanmaktır:

function Get-DeepClone
{
    param(
        $InputObject
    )
    $TempCliXmlString = [System.Management.Automation.PSSerializer]::Serialize(
        $InputObject, [int32]::MaxValue)
    return [System.Management.Automation.PSSerializer]::Deserialize($TempCliXmlString)
}

Son derece büyük karma tablolarda, seri durumdan çıkarma işlevi ölçeği genişletildikçe daha hızlı olur. Ancak, bu yöntemi kullanırken dikkate alınması gereken bazı şeyler vardır. CliXml kullandığından bellek açısından yoğundur ve büyük karma tabloları kopyalıyorsanız, bu bir sorun olabilir. CliXml'nin bir diğer sınırlaması da 48 derinlik sınırlaması olmasıdır. Başka bir deyişle, iç içe yerleştirilmiş karma tablolardan oluşan 48 katmanı olan bir karma tablonuz varsa kopyalama başarısız olur ve hiç karma tablo çıkışı olmaz.

Başka birşey var mı?

Çok hızlı bir şekilde çok yol kat ettim. Umarım bunu her okuyuşunda yeni bir şeyler öğrendiğini veya daha iyi anladığını hissedersin. Bu özelliğin tüm yelpazesini kapsadığım için, şu anda sizin için geçerli olmayabilir. Bu tamamen tamamdır ve PowerShell ile ne kadar çalıştığınıza bağlı olarak beklenen bir durumdur.