การวนซ้ำแฮชหรือใช้อาร์เรย์ใน PowerShell


101

ฉันใช้นี้ (จีน) ก้อนของรหัสที่จะดึงชุดของตารางจาก SQL Server กับBCP

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

มันใช้งานได้ดี แต่ตอนนี้ตารางบางส่วนจำเป็นต้องดึงข้อมูลผ่านมุมมองและบางตารางก็ไม่ ดังนั้นฉันต้องการโครงสร้างข้อมูลดังนี้:

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

ฉันกำลังดูแฮช แต่ฉันไม่พบอะไรเกี่ยวกับวิธีการวนซ้ำแฮช อะไรคือสิ่งที่ถูกต้องที่จะทำที่นี่? บางทีใช้อาร์เรย์ แต่มีทั้งสองค่าคั่นด้วยช่องว่าง?

หรือฉันพลาดอะไรบางอย่างที่ชัดเจน?

คำตอบ:


109

คำตอบของ Christian ทำงานได้ดีและแสดงให้เห็นว่าคุณสามารถวนซ้ำแต่ละรายการในตารางแฮชโดยใช้GetEnumeratorวิธี คุณยังสามารถวนซ้ำโดยใช้keysคุณสมบัติ นี่คือตัวอย่างวิธีการ:

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

เอาท์พุต:

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

ฉันจะแจกแจงแฮชในลำดับอื่นได้อย่างไร เช่นเมื่อฉันต้องการพิมพ์เนื้อหาตามลำดับจากน้อยไปมาก (ที่นี่ a ... b ... c) เป็นไปได้หรือไม่?
LPrc

5
ทำไม$hash.Item($_)แทนที่จะเป็น$hash[$_]?
alvarez

1
@LPrc ใช้เมธอด Sort-Object เพื่อทำเช่นนั้น บทความนี้จะเป็นคำอธิบายที่ดีงามของมันtechnet.microsoft.com/en-us/library/ee692803.aspx
chazbot7

4
คุณสามารถใช้เพียงแค่$hash.$_.
ทีเอ็นที

1
วิธีนี้ใช้ไม่ได้หากแฮชแท็กมีคีย์ = 'คีย์'
Boris Nikitin

201

ไม่แนะนำให้ใช้ชวเลขสำหรับสคริปต์ มันอ่านได้น้อย ตัวดำเนินการ% {} ถือเป็นชวเลข นี่คือวิธีที่ควรทำในสคริปต์เพื่อให้อ่านง่ายและนำกลับมาใช้ใหม่ได้:

การตั้งค่าตัวแปร

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

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

ตัวเลือกที่ 1: GetEnumerator ()

หมายเหตุ: ความชอบส่วนตัว; ไวยากรณ์อ่านง่ายกว่า

GetEnumerator () วิธีการจะทำได้ดังที่แสดง:

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

เอาท์พุต:

c: 3
b: 2
a: 1

ตัวเลือกที่ 2: คีย์

วิธีการคีย์จะทำได้ดังที่แสดง:

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

เอาท์พุต:

c: 3
b: 2
a: 1

ข้อมูลเพิ่มเติม

ระวังการจัดเรียงแฮชแท็กของคุณ ...

Sort-Objectอาจเปลี่ยนเป็นอาร์เรย์:

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

การวนซ้ำ PowerShell นี้และอื่น ๆ มีอยู่ในบล็อกของฉัน


3
เช่นนี้มากขึ้นเพื่อความสามารถในการอ่านโดยเฉพาะอย่างยิ่งสำหรับผู้พัฒนา powershell ที่ไม่ทุ่มเทเช่นฉัน
anIBMer

1
ฉันยอมรับว่าวิธีนี้อ่านง่ายกว่าและอาจจะดีกว่าสำหรับการเขียนสคริปต์
leinad13

2
นอกจากความสามารถในการอ่านแล้วยังมีข้อแตกต่างอย่างหนึ่งระหว่างโซลูชันของ VertigoRay และโซลูชันของ Andy % {} เป็นนามแฝงForEach-Objectซึ่งแตกต่างจากforeachคำสั่งที่นี่ ForEach-Objectใช้ประโยชน์จากไปป์ไลน์ซึ่งอาจเร็วกว่ามากหากคุณทำงานกับไปป์ไลน์อยู่แล้ว foreachคำสั่งไม่ได้; มันเป็นแบบวนซ้ำ
JamesQMurphy

4
@JamesQMurphy ฉันคัดลอกและวางคำตอบโดย @ andy-arismendi จากด้านบน; เป็นวิธีการแก้ปัญหาท่อยอดนิยมสำหรับคำถามนี้ คำพูดของคุณซึ่งทำให้ฉันสนใจมากที่สุดคือForEach-Object(aka:) %{}เร็วกว่าforeach; ผมแสดงให้เห็นว่าเป็นไปไม่ได้เร็วขึ้น ฉันต้องการแสดงให้เห็นว่าคะแนนของคุณถูกต้องหรือไม่ เพราะฉันเหมือนคนส่วนใหญ่ชอบให้โค้ดของฉันทำงานได้เร็วที่สุด ตอนนี้อาร์กิวเมนต์ของคุณกำลังเปลี่ยนเป็นการรันงานแบบขนาน (อาจใช้คอมพิวเตอร์ / เซิร์ฟเวอร์หลายเครื่อง) ... ซึ่งแน่นอนว่าเร็วกว่าการรันงานในชุดจากเธรดเดียว ไชโย!
VertigoRay

1
@JamesQMurphy ฉันยังคิดว่า powershell เป็นไปตามข้อได้เปรียบแบบดั้งเดิมที่เชลล์ให้คุณเมื่อคุณไปป์สตรีมข้อมูลระหว่างกระบวนการต่างๆ: คุณจะได้รับความเท่าเทียมกันตามธรรมชาติจากข้อเท็จจริงง่ายๆที่ว่าแต่ละขั้นตอนไปป์ไลน์เป็นกระบวนการที่เป็นอิสระ (แน่นอนว่าบัฟเฟอร์อาจระบายออกและใน จุดจบท่อส่งทั้งหมดจะช้าที่สุดเท่าที่กระบวนการที่ช้าที่สุด) ซึ่งแตกต่างจากการมีกระบวนการไปป์ไลน์ที่ตัดสินใจด้วยตัวเองเพื่อสร้างเธรดหลายเธรดซึ่งขึ้นอยู่กับรายละเอียดการใช้งานของกระบวนการนั้นเอง
Johan Boulé

8

คุณสามารถทำได้โดยไม่ต้องใช้ตัวแปร

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

สิ่งนี้ทำให้ฉันได้รับ "ForEach-Object: Cannot bind parameter 'Process' ไม่สามารถแปลงค่า" getEnumerator "ของประเภท" System.String "เป็นพิมพ์" System.Management.Automation.ScriptBlock "
luis.espinal

7

เกี่ยวกับการวนซ้ำแฮช:

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

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

6

นี่เป็นอีกวิธีที่รวดเร็วเพียงใช้คีย์เป็นดัชนีในตารางแฮชเพื่อรับค่า:

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

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

5

ฉันชอบตัวแปรนี้ในวิธีการแจงนับด้วยไปป์ไลน์เพราะคุณไม่ต้องอ้างถึงตารางแฮชใน foreach (ทดสอบใน PowerShell 5):

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

เอาท์พุต:

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

ตอนนี้สิ่งนี้ไม่ได้ถูกจัดเรียงตามค่าโดยเจตนาตัวแจงนับเพียงแค่ส่งคืนวัตถุในลำดับย้อนกลับ

แต่เนื่องจากนี่เป็นไปป์ไลน์ตอนนี้ฉันสามารถจัดเรียงวัตถุที่ได้รับจากตัวแจงนับตามค่า:

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

เอาท์พุต:

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

ตัวแจงนับสามารถส่งคืนค่าในลำดับ"ใดก็ได้"เพราะเพียงแค่ย้ำการนำไปใช้งาน ในกรณีนี้จะปรากฏเป็นลำดับย้อนกลับ สำหรับตัวอย่างตอบโต้: $x = @{a = 1; z = 2}; $x.q = 3"เรียงลำดับ" เป็น q, a, z ที่นี่
user2864740


0

หากคุณใช้ PowerShell v3 คุณสามารถใช้ JSON แทนแฮชแท็กและแปลงเป็นวัตถุด้วยConvert-FromJson :

@'
[
    {
        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 คำสั่งคือ ConvertFrom-Json (และ converse, ConvertTo-Json) - เพียงแค่สลับตำแหน่งเส้นประ
atmarx
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.