通过散列进行循环,或者在 PowerShell 中使用数组

我正在使用这段(简化的)代码块来使用 BCP从 SQLServer 提取一组表。

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

I was looking at hashes, but I'm not finding anything on how to loop through a hash. What would be the right thing to do here? Perhaps just use an array, but with both values, separated by space?

Or am I missing something obvious?

265754 次浏览

关于在散列表中循环:

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


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

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

速记不是脚本的首选,它的可读性较差。% {}运算符被认为是速记符。下面是如何在脚本中实现可读性和可重用性:

Variable Setup

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)"
}

Output:

c: 3
b: 2
a: 1

Option 2: Keys

Keys 方法如下所示:

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

Output:

c: 3
b: 2
a: 1

Additional information

Be careful sorting your hashtable...

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 循环可以在我的博客上找到。

If you're using PowerShell v3, you can use JSON instead of a hashtable, and convert it to an object with 转换-从 Json:

@'
[
{
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
}

You can also do this without a variable

@{
'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]);
}

我更喜欢使用管道的枚举器方法的这种变体,因为您不必引用 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

现在,这还没有被有意地按值排序,枚举器只是以相反的顺序返回对象。

But since this is a pipeline, I now can sort the objects received from the enumerator on value:

$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

A short traverse could be given too using the sub-expression operator $( ), which returns the result of one or more statements.

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


forEach($y in $hash.Keys){
Write-Host "$y -> $($hash[$y])"
}

结果:

a -> 1
b -> 2
c -> 3