Цикл через хэш или использование массива в PowerShell?


Я использую этот (упрощенный) кусок кода для извлечения набора таблиц из 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"

Я смотрел на хэши, но я не нашел ничего о том, как перебрать хэш. Что было бы правильным сделать здесь? Возможно просто использовать массив, но с обоими значениями, разделенными космос?

или я упускаю что-то очевидное?

6 71

6 ответов:

ответ Кристиана работает хорошо и показывает, как вы можете перебирать каждый элемент хэш-таблицы с помощью 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

Стенография не является предпочтительной для скриптов; она менее читаема. Оператор %{} считается сокращенным. Вот как это должно быть сделано в скрипте для чтения и повторного использования:

Переменной Установки

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 доступны в моем блоге.

о цикле через хэш:

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

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

вы также можете сделать это без переменной

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

вот еще один быстрый способ, просто используя ключ в качестве индекса в хэш-таблице, чтобы получить значение:

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

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

Если вы используете PowerShell v3, вы можете использовать JSON вместо хэш-таблицы и преобразовать ее в объект с помощью Конвертировать-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
    }