我可以从内循环引用外循环中的迭代 $_ 值吗?

Can I refer to the iterated $_ value in an outer loop from the inner loop?

出于演示目的,我有一个 JSON 文档,其中列出了大洲和每个大洲的一些国家:

$json = ConvertFrom-Json '[{"Continent" : "Europe","Countries" : ["UK","France","Germany"]},
                           {"Continent" : "Africa","Countries" : ["Mali","Malawi","Nigeria"]}]' 

如果我想获得其中所有国家/地区的列表,我可以通过管道连接到 foreach cmdlet 两次,在 PowerShell 的一行中完成此操作:

$json | foreach {$_.Countries} | foreach {$_ | Select @{Name="Country";Expression={$_}}}
Country                                                               
------- UK                                                                     France                                                                 Germany                                                                Mali                                                                   Malawi                                                                 Nigeria

一切都很正常。但是,如果我也想 Select 每个国家/地区的大陆,那么我需要一个 multi-statement 脚本块:

$json | foreach {
    $Continent = $_.Continent
    $_.Countries | foreach {
                            $_ | Select-Object @{Name="Continent";Expression={$Continent}},@{Name="Country";Expression={$_}}
                           }
} 
Continent     Country
---------     ------- 
Europe        UK      
Europe        France  
Europe        Germany 
Africa        Mali    
Africa        Malawi  
Africa        Nigeria

我喜欢 PowerShell 的简洁性,所以在第二个示例中,我对需要将迭代大陆存储在变量中这一事实感到有些泄气。我想知道的是,我可以让它变得更好吗?我可以让它更简洁吗?有没有办法从对 Select-Object?

的调用中引用外循环中的迭代值

仅此而已,基本上只是想了解更多有关 PowerShell 及其功能的信息。

我认为不可能得到 "outer" $_。而且,就算可以,我觉得也不是什么好主意

您可以使用 Get-Variable -Scope 1 在 Powershell 中(递归地)引用父作用域,其中 1 是直接父作用域,但这在嵌套的 ForEach-Object 调用中似乎不起作用。有关详细信息,请参阅 about_Scopes

在我看来,尽管为了可读性而编写代码通常更好。

如果您被多语句块打扰的主要原因是它在多行中,您可以用 ; 分隔语句以使它们保持在同一行。

$json | foreach { $Continent = $_.Continent ; $_.Countries | foreach { $_ | Select-Object @{Name="Continent";Expression={$Continent}},@{Name="Country";Expression={$_}} }} 

就个人而言,我更喜欢分解的。

您不能直接访问外部 $_ 的值,但您可以使用 param:

捕获它的值
param($x=$_)

从技术上讲,这将使您能够在一行中完成所有操作:

$json | foreach { param($x=$_) $_.Countries | foreach { $_ | Select-Object @{Name="Continent";Expression={$x.Continent}},@{Name="Country";Expression={$_}}}}

您还可以通过使用一些标准别名(即 % 代表 foreachselect 代表 Select-Object)来减少代码的大小:

$json | % { param($x=$_) $_.Countries | % { $_ | select @{Name="Continent";Expression={$x.Continent}},@{Name="Country";Expression={$_}}}}

但我只建议这样做是为了在控制台中快速运行。将打包且无法实现的代码投入生产简直就是罪恶。 ;)