【问题标题】:How does $_ work when piping in PowerShell?在 PowerShell 中管道时 $_ 是如何工作的?
【发布时间】:2019-05-25 02:57:48
【问题描述】:

我对 $_ 变量在某些管道环境中的工作方式感到困惑。在此示例中用于备份 Bitlocker 密钥:

Get-BitlockerVolume | % {$_.KeyProtector | ? RecoveryPassword | Backup-BitlockerKeyProtector -MountPoint $_.MountPoint}

我是这样读英文的:

  • 获取所有 BitLockerVolume 对象
  • 对于每个 BitLockerVolume 对象,将 KeyProtector 字段通过管道转发
  • 管道 KeyProtector 对象进一步转发给具有 RecoverPassword 的对象
  • 运行 Backup-BitlockerKeyProtector,并提供 MountPoint

但是,MountPoint 是 BitLockerVolume 对象的一个​​字段,如下所示:

PS C:\Windows\system32> Get-BitLockerVolume | Get-Member | Where-Object {$_.Name -eq "MountPoint"}


   TypeName: Microsoft.BitLocker.Structures.BitLockerVolume

Name       MemberType Definition
----       ---------- ----------
MountPoint Property   string MountPoint {get;}

那么,对于包裹在方括号 { } 中的整个块,$_ 变量在任何数量的管道中总是相同吗?例如,我们正在转发的对象正在改变。它不再是 BitLockerVolume 对象,而是 KeyProtector 对象。那么在这种情况下,$_ 会始终引用 BitLockerVolume 对象,还是会根据通过链进一步传输的不同类型的对象而在管道中进一步更改?

【问题讨论】:

  • 我投了反对票,因为网上和 StackOverflow 上有大量信息:stackoverflow.com/questions/3494115/…
  • @I.TDelinquent 除了我用管道的具体示例和场景询问外,不仅仅是通用的“我使用 $_ 打印数字”
  • @I.TDelinquent 很明显,我没有写过关于“什么是 $_”的帖子,对此有所了解,并询问了具体的上下文。
  • $_ 指的是来自最后一个管道的对象。
  • @montonero 这就是我感到困惑的地方。所以在这种情况下,最后一个管道部分中的 $PSItem(又名 $_)对象应该是 KeyProtector,而不是实际上的 BitLockerVolume 对象?如果是这种情况,那么我的 $_.MountPoint 将不起作用,因为这是管道中较早的原始对象的字段,而不是 KeyProtector 的字段。

标签: powershell pipe


【解决方案1】:

所以 $_ 是来自当前管道的信息。

1,2 | %{
    $_
}

回复

1
2

同时

1,2 | %{
    "a","b" | %{
        $_
    }
}

回复

a
b
a
b

我们首先可以看到 %_ 的输出来自给定的最后一个信息,即1,2。虽然下一个示例仍然循环 1,2,但输出来自 a,b 内的管道。

可以通过将第一个管道信息存储到第二个管道中的变量中来解决此问题

1,2 | %{
    $Num = $_
    "a","b" | %{
        $Num
    }
}

输出是什么情况

1
1
2
2

在你给出的例子中,让我们看看它的格式

Get-BitlockerVolume | % {
    $_.KeyProtector | ? RecoveryPassword | Backup-BitlockerKeyProtector -MountPoint $_.MountPoint
}

您有 2 个不同的管道。首先是获得“BitlockerVolumevolume”。 第二个从你发送BitlockerVolume's KeyProtector开始。

就像说

对于每个 Bitlocker 卷,获取 KeyProtector。

对于每个 KeyProtector,获取具有成员 RecoveryPassword 的 KeyProtector

Foreach KeyProtector with member RecoveryPassword, Backup Bitlocker Key Protector Using KeyProtector's Mountpoints

所以最后一点,我还假设您给出的示例行不通。 您可能正在寻找的是这个......

Get-BitlockerVolume | % {
    $MountPoint = $_.MountPoint
    $_.KeyProtector | ? RecoveryPassword | Backup-BitlockerKeyProtector -MountPoint $MountPoint -KeyProtectorId $_.KeyProtectorId
}

【讨论】:

  • 我明白了。所以这与我的例子有点不同,但我认为这是有道理的。所以在你的第二个例子中,有数字和字符,你有两个块包裹在 { } 括号中。所以 $PSItem aka $_ 的上下文范围是块,对吗?在我的示例中,$_ 变量仅包含在一组括号内,因此无论管道如何,它都应该保持不变,对吗?
  • 您能否再补充一点信息。我仍然对 $_ 在 $_.MountPoint 处所指的内容感到有些困惑。那是 BitLockerVolume 对象还是 KeyProtector 对象?尽管您正在展示,但其他一切都是有意义的。
  • 这样更好吗?
  • 我已经给你答案了,但是在“使用 KeyProtector 的挂载点”部分仍然有些混乱。可能只是措辞。当然,这在技术上是 KeyProtector 的 MountPoint,但在执行 $_.MountPoint 时,与其他答案和 cmets 相比,我相信 $PSItem 变量又名 $_,在技术上是 BitlockerVolumeObject。这是我一直以来的主要困惑。根据您的回答,听起来 $_.MountPoint 正在从 KeyProtector 对象访问 MountPoint 字段。 "使用 KeyProtector 的挂载点"
  • 在您给出的示例中。根据您的示例,Mountpoint 位于keyprotector.mountpoint
【解决方案2】:

让我们扩展别名并填写隐含参数。 $_ 只能在作为 cmdlet 选项的脚本块“{}”内使用。仅仅因为您在管道中,并不意味着您可以使用 $_ 。这里的 $_ 属于 Foreach-Object。 Where-Object 使用的是比较语句。

Get-BitlockerVolume | Foreach-Object -Process {
  $_.KeyProtector | Where-Object -Property RecoveryPassword | 
    Backup-BitlockerKeyProtector -MountPoint $_.MountPoint
}

【讨论】:

  • 因此,即使通过管道传递的对象正在发生变化,$PSItem aka $_ 在整个脚本块中是否仍然保持不变?因此,即使 KeyProtector 对象被传递到管道的最后一段,我们仍然可以在此时通过 $PSItem 变量引用原始 BitlockerVolume 对象?
  • 这对我来说是有道理的,但是这个线程上有多个 cmets 指出它指的是通过管道传递的对象,此时它将是 KeyProtector。不用说我还是很困惑
  • 是通过管道传递给foreach-object的对象。
  • 好的,我知道它最初是通过管道从 Get-BitlockerVolume 传递到 foreach-object 的项目。我只是想确认在整个 Foreach-Object 脚本块部分中,无论该脚本块中的管道如何,$_ 都将保持不变。
【解决方案3】:

我知道这里已经有了很好的答案,但我觉得一个重要的问题没有得到解决。关于$_ 在整个Foreach-Object {}存在嵌套时会发生什么的问题。我将使用 ArcSet 的示例,因为这是选择的答案。

1,2 | % {
    "$_ before second foreach"
    'a','b' | % {
        "$_ inside second foreach"
    }
    "$_ after second foreach"
}

1 before second foreach
a inside second foreach
b inside second foreach
1 after second foreach
2 before second foreach
a inside second foreach
b inside second foreach
2 after second foreach

注意$_ 成为Foreach-Object {} 块中的代码正在处理的当前对象。当进入第二个Foreach-Object 块时,$_ 会发生变化。退出第二个Foreach-Object 块时,$_ 变回继续 由第一个块的其余部分处理的对象。所以$_ 在块处理过程中既不会保持不变也不会丢失。您需要将$_ 分配为另一个变量,或者在适用的情况下使用-PipelineVariable 开关来访问不同块中的这些对象。

【讨论】:

    【解决方案4】:

    我喜欢在 ArcSet 的 answer 上构建一点点。由于我终于明白$PSItem 的值会随着管道类型的变化而变化,所以我运行了这段代码来做一些检查。

    Get-BitLockerVolume | % {$_.GetType()}
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     False    BitLockerVolume                          System.Object
    True     False    BitLockerVolume                          System.Object
    True     False    BitLockerVolume                          System.Object
    

    在这里我们可以看到管道返回的一些对象是BitLockerVolume 类型。

    现在,根据我最初的问题/示例,如果我们根据KeyProtector 进一步管道它,$PSItem 变量的对象类型将发生变化。

    Get-BitLockerVolume | % { $_.KeyProtector | ? RecoveryPassword  | % {$_.GetType()}}
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     False    BitLockerVolumeKeyProtector              System.Object
    True     False    BitLockerVolumeKeyProtector              System.Object
    

    所以此时,在管道的末尾,我们执行一些其他的 cmdlet,例如 Backup-BitlockerKeyProtector 并引用 $PSItem 变量,也就是 $_,然后它将引用最后通过管道的对象类型,在这种情况下,对象将是 BitLockerVolumeKeyProtector 类型。

    【讨论】:

      猜你喜欢
      • 2010-11-07
      • 2012-12-25
      • 2014-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-08
      • 2014-11-19
      • 2011-07-16
      相关资源
      最近更新 更多