해시를 반복하거나 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.