해시를 반복하거나 PowerShell에서 배열 사용


96

이 (단순화 된) 코드 청크를 사용하여 BCP로 SQL Server에서 테이블 집합을 추출하고 있습니다.

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

해시를보고 있었지만 해시를 반복하는 방법에 대해서는 아무것도 찾지 못했습니다. 여기서 옳은 일은 무엇일까요? 아마도 배열을 사용하지만 두 값을 공백으로 구분하여 사용합니까?

아니면 분명한 것을 놓치고 있습니까?

답변:


108

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.$_.
TNT

1
이 접근 방식은 해시 테이블에 key = 'Keys'가 포함 된 경우 작동하지 않습니다.
Boris Nikitin

195

속기는 스크립트에 선호되지 않습니다. 읽기 어렵습니다. % {} 연산자는 속기로 간주됩니다. 가독성과 재사용 성을 위해 스크립트에서 수행하는 방법은 다음과 같습니다.

변수 설정

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 : 키

Keys 메서드는 다음과 같이 수행됩니다.

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 2014-08-20

1
이 방법이 읽기 쉽고 스크립팅에 더 적합하다는 데 동의합니다.
leinad13 2014 년

2
가독성을 제외하고 VertigoRay의 솔루션과 Andy의 솔루션 간에는 한 가지 차이점이 있습니다. % {}는에 대한 별칭이며 여기에 ForEach-Object있는 foreach문과 다릅니다 . ForEach-Object이미 파이프 라인으로 작업중인 경우 훨씬 더 빠를 수있는 파이프 라인을 사용합니다. foreach문은하지 않습니다; 간단한 루프입니다.
JamesQMurphy

4
@JamesQMurphy 위에서 @ andy-arismendi가 제공 한 답변을 복사하여 붙여 넣었습니다. 이 질문에 대한 대중적인 파이프 라인 솔루션입니다. 제 관심을 끌었던 귀하의 진술은 ForEach-Object(일명 :) %{}이보다 빠르다는 것입니다 foreach. 나는 그것이 더 빠르지 않다는 것을 보여 주었다 . 귀하의 요점이 유효한지 여부를 보여주고 싶었습니다. 나는 대부분의 사람들과 마찬가지로 코드가 최대한 빨리 실행되는 것을 좋아하기 때문입니다. 이제 당신의 주장은 병렬로 작업을 실행하는 것으로 바뀌고 있습니다 (잠재적으로 여러 컴퓨터 / 서버를 사용) ... 물론 단일 스레드에서 작업을 직렬로 실행하는 것보다 빠릅니다. 건배!
VertigoRay

1
@JamesQMurphy 또한 powershell이 ​​여러 프로세스간에 데이터 스트림을 파이프 할 때 쉘이 제공하는 전통적인 이점을 따른다고 생각합니다. 각 파이프 라인 단계가 독립적 인 프로세스라는 단순한 사실에서 자연스러운 병렬 처리를 얻을 수 있습니다 (물론 버퍼가 결국 전체 파이프 라인은 가장 느린 프로세스만큼 느려집니다.) 이는 프로세스 자체의 구현 세부 사항에 전적으로 의존하는 여러 스레드를 생성하도록 자체적으로 결정하는 파이프 라인 프로세스를 갖는 것과 다릅니다.
요한 보울

8

변수 없이도이 작업을 수행 할 수 있습니다.

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

이것은 나에게 "를 ForEach-개체 :하지 바인드 매개 변수 '프로세스'는 변환 할 수 없습니다 수 없습니다."얻는다 선택 System.String GetEnumerator를 "유형의 값을"System.Management.Automation.ScriptBlock "를 입력하려면" "
luis.espinal

6

해시를 반복하는 방법 :

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

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

5

다음은 값을 얻기 위해 키를 해시 테이블에 대한 인덱스로 사용하는 또 다른 빠른 방법입니다.

$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 (및 반대 인 ConvertTo-Json)입니다. 대시 위치를 바꾸면됩니다.
atmarx
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.