Bir karma üzerinden döngü veya PowerShell'de bir dizi kullanma


101

BCP ile SQL Server'dan bir dizi tablo çıkarmak için bu (basitleştirilmiş) kod yığınını kullanıyorum .

$OutputDirectory = "c:\junk\"
$ServerOption =   "-SServerName"
$TargetDatabase = "Content.dbo."

$ExtractTables = @(
    "Page"
    , "ChecklistItemCategory"
    , "ChecklistItem"
)

for ($i=0; $i -le $ExtractTables.Length – 1; $i++)  {
    $InputFullTableName = "$TargetDatabase$($ExtractTables[$i])"
    $OutputFullFileName = "$OutputDirectory$($ExtractTables[$i])"
    bcp $InputFullTableName out $OutputFullFileName -T -c $ServerOption
}

Harika çalışıyor, ancak şimdi bazı tabloların görünümler yoluyla çıkarılması gerekiyor ve bazılarının çıkarılmaması gerekiyor. Bu yüzden şuna benzer bir veri yapısına ihtiyacım var:

"Page"                      "vExtractPage"
, "ChecklistItemCategory"   "ChecklistItemCategory"
, "ChecklistItem"           "vExtractChecklistItem"

Karmalara bakıyordum, ancak bir hash üzerinden nasıl döngü yapılacağına dair hiçbir şey bulamıyorum. Burada yapılacak doğru şey ne olabilir? Belki sadece bir dizi kullanın, ancak her iki değer de boşlukla ayrılmış mı?

Yoksa bariz bir şeyi mi kaçırıyorum?

Yanıtlar:


109

Christian'ın cevabı işe yarıyor ve GetEnumeratoryöntemi kullanarak her bir karma tablo öğesinde nasıl döngü yapabileceğinizi gösteriyor . keysÖzelliği kullanarak da döngü yapabilirsiniz . İşte nasıl yapılacağına dair bir örnek:

$hash = @{
    a = 1
    b = 2
    c = 3
}
$hash.Keys | % { "key = $_ , value = " + $hash.Item($_) }

Çıktı:

key = c , value = 3
key = a , value = 1
key = b , value = 2

Hash'i başka bir sırada nasıl sıralayabilirim? Örneğin, içeriğini artan sırada yazdırmak istediğimde (burada a ... b ... c). Hatta mümkün mü?
LPrc

5
Neden $hash.Item($_)yerine $hash[$_]?
alvarez

1
@LPrc bunu yapmak için Sort-Object yöntemini kullanır. : Bu makale oldukça bunun iyi bir açıklama yapan technet.microsoft.com/en-us/library/ee692803.aspx
chazbot7

4
Sadece kullanabilirsin $hash.$_.
TNT

1
Bu yaklaşım, hashtable anahtar = 'Anahtarlar' içeriyorsa çalışmaz.
Boris Nikitin

201

Komut dosyaları için steno tercih edilmez; daha az okunabilir. % {} Operatörü steno olarak kabul edilir. Okunabilirlik ve yeniden kullanılabilirlik için bir komut dosyasında yapılması gerekenler:

Değişken Kurulum

PS> $hash = @{
    a = 1
    b = 2
    c = 3
}
PS> $hash

Name                           Value
----                           -----
c                              3
b                              2
a                              1

Seçenek 1: GetEnumerator ()

Not: kişisel tercih; sözdiziminin okunması daha kolaydır

GetEnumerator () yöntemi gösterildiği gibi yapılır:

foreach ($h in $hash.GetEnumerator()) {
    Write-Host "$($h.Name): $($h.Value)"
}

Çıktı:

c: 3
b: 2
a: 1

Seçenek 2: Tuşlar

Keys yöntemi gösterildiği gibi yapılacaktır:

foreach ($h in $hash.Keys) {
    Write-Host "${h}: $($hash.Item($h))"
}

Çıktı:

c: 3
b: 2
a: 1

Ek bilgi

Hashtable'ınızı sıralarken dikkatli olun ...

Sort-Object onu bir diziye değiştirebilir:

PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object


PS> $hash = $hash.GetEnumerator() | Sort-Object Name
PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Bu ve diğer PowerShell döngüleri blogumda mevcuttur.


3
özellikle benim gibi powershell geliştiricilerini adamamak için bu bir tane daha okunabilirlik için.
anIBMer

1
Bu yöntemin okunmasının daha kolay olduğunu ve bu nedenle muhtemelen komut dosyası için daha iyi olduğunu kabul ediyorum.
leinad13

2
Okunabilirlik bir yana, VertigoRay'in çözümü ile Andy'nin çözümü arasında bir fark var. % {} ForEach-Objectburadaki foreachifadeden farklı olan bir takma addır . ForEach-Objectzaten bir ardışık düzen ile çalışıyorsanız çok daha hızlı olabilen ardışık düzeni kullanır. foreachİfadesi değildir; bu basit bir döngü.
JamesQMurphy

4
@JamesQMurphy @ andy-arismendi tarafından sağlanan cevabı yukarıdan kopyalayıp yapıştırdım; bu soru için popüler boru hattı çözümüdür. İlgimi doruğa İfadende, olmasıydı ForEach-Object: (aka %{}daha hızlı olduğu) foreach; Bunun daha hızlı olmadığını gösterdim . Konunuzun geçerli olup olmadığını göstermek istedim; çünkü çoğu insan gibi, kodumun olabildiğince hızlı çalışmasını seviyorum. Şimdi, argümanınız işleri paralel olarak çalıştırmaya (potansiyel olarak birden fazla bilgisayar / sunucu kullanarak) değişiyor ... ki bu, elbette bir işi tek bir iş parçacığından seri olarak çalıştırmaktan daha hızlıdır. Şerefe!
VertigoRay

1
@JamesQMurphy Ayrıca powershell'in, çeşitli süreçler arasında veri akışlarını yönlendirdiğinizde kabukların size sağladığı geleneksel avantajı takip ettiğini düşünüyorum: her boru hattı aşamasının bağımsız bir süreç olduğu basit gerçeğinden doğal paralellik elde edersiniz (tabii ki, bir tampon boşalabilir ve sonunda tüm boru hattı en yavaş süreci kadar yavaş ilerliyor). Bu, tamamen işlemin kendisinin uygulama ayrıntılarına bağlı olan birden fazla iş parçacığı oluşturmaya kendi başına karar veren bir ardışık düzen sürecine sahip olmaktan farklıdır.
Johan Boulé

8

Bunu bir değişken olmadan da yapabilirsiniz

@{
  'foo' = 222
  'bar' = 333
  'baz' = 444
  'qux' = 555
} | % getEnumerator | % {
  $_.key
  $_.value
}

Bu bana bir "ForEach-Object: 'Process' parametresi bağlanamıyor." System.String "türündeki" getEnumerator "değeri" System.Management.Automation.ScriptBlock "türüne dönüştürülemiyor
luis.espinal

7

Bir hash üzerinden döngü yapmak hakkında:

$Q = @{"ONE"="1";"TWO"="2";"THREE"="3"}
$Q.GETENUMERATOR() | % { $_.VALUE }
1
3
2

$Q.GETENUMERATOR() | % { $_.key }
ONE
THREE
TWO

6

İşte başka bir hızlı yol, değeri almak için anahtarı hash tablosuna bir dizin olarak kullanmak:

$hash = @{
    'a' = 1;
    'b' = 2;
    'c' = 3
};

foreach($key in $hash.keys) {
    Write-Host ("Key = " + $key + " and Value = " + $hash[$key]);
}

5

Bu değişkeni bir ardışık düzen ile numaralandırıcı yönteminde tercih ediyorum, çünkü foreach'deki karma tablosuna başvurmanız gerekmiyor (PowerShell 5'te test edilmiştir):

$hash = @{
    'a' = 3
    'b' = 2
    'c' = 1
}
$hash.getEnumerator() | foreach {
    Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}

Çıktı:

Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3

Şimdi, bu kasıtlı olarak değere göre sıralanmamıştır, numaralandırıcı basitçe nesneleri ters sırada döndürür.

Ancak bu bir ardışık düzen olduğundan, şimdi numaralandırıcıdan alınan nesneleri değere göre sıralayabilirim:

$hash.getEnumerator() | sort-object -Property value -Desc | foreach {
  Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}

Çıktı:

Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1

Numaralandırıcı , temeldeki uygulamayı basitçe yinelediği için değerleri "herhangi bir" sırada döndürebilir . Bu durumda, ters sırada görünmektedir . Bir karşı örnek için: $x = @{a = 1; z = 2}; $x.q = 3burada q, a, z şeklinde "sıralanmıştır".
user2864740


0

PowerShell v3 kullanıyorsanız, bir hashtable yerine JSON kullanabilir ve bunu Convert-FromJson ile bir nesneye dönüştürebilirsiniz :

@'
[
    {
        FileName = "Page";
        ObjectName = "vExtractPage";
    },
    {
        ObjectName = "ChecklistItemCategory";
    },
    {
        ObjectName = "ChecklistItem";
    },
]
'@ | 
    Convert-FromJson |
    ForEach-Object {
        $InputFullTableName = '{0}{1}' -f $TargetDatabase,$_.ObjectName

        # In strict mode, you can't reference a property that doesn't exist, 
        #so check if it has an explicit filename firest.
        $outputFileName = $_.ObjectName
        if( $_ | Get-Member FileName )
        {
            $outputFileName = $_.FileName
        }
        $OutputFullFileName = Join-Path $OutputDirectory $outputFileName

        bcp $InputFullTableName out $OutputFullFileName -T -c $ServerOption
    }

nb komut ConvertFrom-Json (ve converse, ConvertTo-Json) - sadece çizgi yerleşimini değiştirin.
atmarx
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.