Vòng qua một hàm băm hoặc sử dụng một mảng trong PowerShell


96

Tôi đang sử dụng đoạn mã (đơn giản hóa) này để trích xuất một tập hợp các bảng từ SQL Server với 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
}

Nó hoạt động tốt, nhưng bây giờ một số bảng cần được trích xuất qua các khung nhìn, và một số thì không. Vì vậy, tôi cần một cấu trúc dữ liệu như sau:

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

Tôi đã xem xét các hàm băm, nhưng tôi không tìm thấy bất cứ điều gì về cách lặp qua một hàm băm. Điều gì sẽ là điều đúng đắn để làm ở đây? Có lẽ chỉ sử dụng một mảng, nhưng với cả hai giá trị, được phân tách bằng dấu cách?

Hay tôi đang thiếu một cái gì đó rõ ràng?

Câu trả lời:


108

Câu trả lời của Christian hoạt động tốt và cho thấy cách bạn có thể lặp lại từng mục trong bảng băm bằng GetEnumeratorphương pháp này. Bạn cũng có thể lặp lại bằng cách sử dụng thuộc keystính. Đây là một ví dụ về cách:

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

Đầu ra:

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

Làm cách nào để liệt kê hàm băm theo một thứ tự khác? Ví dụ: khi tôi muốn in nội dung của nó theo thứ tự tăng dần (ở đây là a ... b ... c). Nó thậm chí có thể?
LPrc

5
Tại sao $hash.Item($_)thay vì $hash[$_]?
alvarez, 9/11/17

1
@LPrc sử dụng phương thức Sắp xếp-Đối tượng để làm điều đó. Bài viết này giải thích khá tốt về nó: technet.microsoft.com/en-us/library/ee692803.aspx
chazbot7

4
Bạn thậm chí có thể sử dụng chỉ $hash.$_.
TNT

1
Cách tiếp cận này không hoạt động nếu bảng băm chứa key = 'Keys'.
Boris Nikitin

195

Tốc ký không được ưu tiên cho các tập lệnh; nó ít đọc hơn. Toán tử% {} được coi là viết tắt. Dưới đây là cách nó nên được thực hiện trong một tập lệnh để có thể đọc và tái sử dụng:

Thiết lập biến

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

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

Tùy chọn 1: GetEnumerator ()

Lưu ý: sở thích cá nhân; cú pháp dễ đọc hơn

Phương thức GetEnumerator () sẽ được thực hiện như hình:

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

Đầu ra:

c: 3
b: 2
a: 1

Tùy chọn 2: Phím

Phương thức Keys sẽ được thực hiện như được hiển thị:

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

Đầu ra:

c: 3
b: 2
a: 1

Thông tin thêm

Hãy cẩn thận sắp xếp bảng băm của bạn ...

Sort-Object có thể thay đổi nó thành một mảng:

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

Điều này và vòng lặp PowerShell khác có sẵn trên blog của tôi.


3
như thế này nữa để dễ đọc, đặc biệt đối với những nhà phát triển không dành riêng cho các nhà phát triển powershell như tôi.
anIBMer

1
Tôi đồng ý rằng phương pháp này dễ đọc hơn và do đó có lẽ tốt hơn cho việc viết kịch bản.
leinad 13

2
Ngoài khả năng đọc, có một điểm khác biệt giữa giải pháp của VertigoRay và giải pháp của Andy. % {} là một bí danh ForEach-Object, khác với foreachcâu lệnh ở đây. ForEach-Objectsử dụng đường ống, có thể nhanh hơn nhiều nếu bạn đã làm việc với đường ống. Các foreachtuyên bố không; đó là một vòng lặp đơn giản.
JamesQMurphy

4
@JamesQMurphy Tôi sao chép và dán câu trả lời được cung cấp bởi @ andy -osystemmendi từ phía trên; nó là giải pháp đường ống phổ biến cho câu hỏi này. Tuyên bố của bạn, khiến tôi quan tâm nhất, là ForEach-Object(còn gọi là %{}:) nhanh hơn foreach; Tôi đã cho thấy điều đó không nhanh hơn . Tôi muốn chứng minh xem quan điểm của bạn có hợp lệ hay không; bởi vì tôi, giống như hầu hết mọi người, muốn mã của tôi chạy nhanh nhất có thể. Bây giờ, lập luận của bạn đang chuyển sang chạy các công việc song song (có khả năng sử dụng nhiều máy tính / máy chủ) ... tất nhiên là nhanh hơn chạy một công việc hàng loạt từ một luồng đơn lẻ. Chúc mừng!
VertigoRay

1
@JamesQMurphy Tôi cũng sẽ nghĩ rằng powershell tuân theo lợi thế truyền thống mà shell mang lại cho bạn khi bạn chuyển luồng dữ liệu giữa một số quy trình: bạn nhận được sự song song tự nhiên từ thực tế đơn giản rằng mỗi giai đoạn đường ống là một quy trình độc lập (tất nhiên, bộ đệm có thể thoát và trong cuối cùng, toàn bộ đường ống diễn ra chậm như quá trình chậm nhất của nó). Điều đó khác với việc có một quy trình đường ống tự quyết định tạo ra nhiều luồng, điều này hoàn toàn phụ thuộc vào chi tiết triển khai của chính quy trình đó.
Johan Boulé

8

Bạn cũng có thể làm điều này mà không cần biến

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

Điều này được cho tôi một "ForEach-Object: Không thể bind tham số 'Process' Không thể chuyển đổi. 'GetEnumerator' giá trị của loại 'System.String' gõ 'System.Management.Automation.ScriptBlock'
luis.espinal

6

Giới thiệu về lặp qua hàm băm:

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

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

5

Tôi thích biến thể này hơn trên phương pháp điều tra viên có đường dẫn, vì bạn không phải tham khảo bảng băm trong foreach (được thử nghiệm trong PowerShell 5):

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

Đầu ra:

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

Bây giờ, điều này không được sắp xếp theo giá trị một cách có chủ ý, người điều tra chỉ cần trả về các đối tượng theo thứ tự ngược lại.

Nhưng vì đây là một đường dẫn, bây giờ tôi có thể sắp xếp các đối tượng nhận được từ trình điều tra theo giá trị:

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

Đầu ra:

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

Điều tra viên có thể trả về các giá trị theo thứ tự "bất kỳ" , vì nó chỉ cần lặp lại việc triển khai bên dưới. Trong trường hợp này, nó có vẻ là thứ tự ngược lại. Đối với ví dụ ngược lại: $x = @{a = 1; z = 2}; $x.q = 3được "sắp xếp" là q, a, z ở đây.
user2864740, 10/07/19


4

Đây là một cách nhanh chóng khác, chỉ cần sử dụng khóa làm chỉ mục vào bảng băm để nhận giá trị:

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

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

0

Nếu bạn đang sử dụng PowerShell v3, bạn có thể sử dụng JSON thay vì bảng băm và chuyển đổi nó thành một đối tượng bằng 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 lệnh là ConvertFrom-Json (và converse, ConvertTo-Json) - chỉ cần hoán đổi vị trí dấu gạch ngang.
atmarx
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.