循环遍历哈希,或在 PowerShell 中使用数组

Posted

技术标签:

【中文标题】循环遍历哈希,或在 PowerShell 中使用数组【英文标题】:Looping through a hash, or using an array in PowerShell 【发布时间】:2012-02-19 08:53:32 【问题描述】:

我正在使用这段(简化的)代码从带有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"

我正在查看哈希,但我没有找到任何关于如何循环遍历哈希的信息。在这里做什么是正确的?也许只使用一个数组,但两个值都用空格分隔?

还是我遗漏了一些明显的东西?

【问题讨论】:

【参考方案1】:

脚本不推荐使用简写;它的可读性较差。 % 运算符被认为是简写。为了可读性和可重用性,在脚本中应该这样做:

变量设置

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

This and other PowerShell looping are available on my blog.

【讨论】:

为了可读性而更喜欢这个,尤其是对于像我这样不专注于 powershell 的开发人员。 我同意这种方法更容易阅读,因此可能更适合编写脚本。 抛开可读性不谈,VertigoRay 的解决方案和 Andy 的解决方案有一个区别。 % 是ForEach-Object 的别名,不同于这里的foreach 语句。 ForEach-Object 使用管道,如果您已经在使用管道,它可以更快。 foreach 声明没有;这是一个简单的循环。 @JamesQMurphy 根据我的测试,我没有看到任何支持您声称管道更快的说法:gist.github.com/VertigoRay/3bb0166d6a877839b420 @JamesQMurphy 我从上面复制并粘贴了@andy-arismendi 提供的答案;这是这个问题的流行管道解决方案。您的陈述引起了我的极大兴趣,即ForEach-Object(又名:%)比foreach 快; 我证明这并不快。我想证明你的观点是否有效;因为我和大多数人一样,喜欢我的代码尽可能快地运行。现在,您的论点正在更改为并行运行作业(可能使用多台计算机/服务器)......这当然比从单个线程串行运行作业要快。干杯!【参考方案2】:

也可以使用子表达式运算符 $( ) 进行短遍历,它返回一个或多个语句的结果。

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

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

结果:

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

【讨论】:

【参考方案3】:

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

【讨论】:

枚举器可以按 "any" 顺序返回值,因为它只是迭代底层实现。在这种情况下,它恰好出现是相反的顺序。举个反例:$x = @a = 1; z = 2; $x.q = 3 在此处被“排序”为 q,a,z。 "Ordered dictionaries differ from hash tables in that the keys always appear in the order in which you list them. The order of keys in a hash table is not determined - [ordered] 属性触发选择不同的实现。【参考方案4】:

关于遍历哈希:

$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]);

【讨论】:

【参考方案6】:

你也可以在没有变量的情况下这样做

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

【讨论】:

这给我一个“ ForEach-Object : 无法绑定参数 'Process'。无法将类型“System.String”的“getEnumerator”值转换为类型“System.Management.Automation.ScriptBlock” 【参考方案7】:

如果您使用的是 PowerShell v3,则可以使用 JSON 代替哈希表,并将其转换为带有 Convert-FromJson 的对象:

@'
[
    
        FileName = "Page";
        ObjectName = "vExtractPage";
    ,
    
        ObjectName = "ChecklistItemCategory";
    ,
    
        ObjectName = "ChecklistItem";
    ,
]
'@ | 
    Convert-FromJson |
    ForEach-Object 
        $InputFullTableName = '01' -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
    

【讨论】:

n.b.命令是 ConvertFrom-Json(以及相反的 ConvertTo-Json)——只需交换破折号的位置。【参考方案8】:

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)。有可能吗? 为什么是$hash.Item($_) 而不是$hash[$_] @LPrc 使用 Sort-Object 方法来做到这一点。这篇文章对它做了很好的解释:technet.microsoft.com/en-us/library/ee692803.aspx 你甚至可以只使用$hash.$_ 如果 hashtable 包含 key = 'Keys',则此方法不起作用。

以上是关于循环遍历哈希,或在 PowerShell 中使用数组的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 Powershell 中创建一个数组并在 Azure DevOps YAML 中循环遍历它?

HashTable中的元素可以用for循环去遍历吗?

node js redis循环遍历每个哈希键值

powershell 这将循环遍历SharePoint列表中的所有项并更新该值。在这种情况下,它将标题从小写更改为大写

遍历对象哈希表

c#用foreach遍历数组、列表时是直接获得数据元素,而foreach哈希表时,为啥获得的是命名空间名??