本地语句输出与调用命令输出非常不同

Local statement output very different than invoke-command output

直接登录系统,我运行这个语句,得到这个输出:

(Get-ClusterNetwork 'cluster backups').role
None

这是完美的...甚至美丽,简单。

然而,当我 运行 使用 invoke-command 从远程机器发出完全相同的语句时,直到现在我一直只是假设就像在机器的 CLI 中输入这个确切的语句,我得到此输出改为

Invoke-Command -Session $hi -ScriptBlock {(Get-ClusterNetwork 'cluster backups').role}
PSComputerName RunspaceId                           Value
-------------- ----------                           -----
dumdum a84b6c89-dumdum-80d3-ed43230ee8ab            None

下面是真正有趣的事情。如果我为 invoke-command 输出分配一个变量,它将具有与上面显示的相同的输出,除非 - 我将其通过管道传输到 set-clipboard

所以变量

$hello = invoke-command -session $hi -scriptblock {(get-networkcluster 'cluster backups').role}

现在在提示符中输入 $hello,我得到:

PSComputerName RunspaceId                           Value
-------------- ----------                           -----
dumdum a84b6c89-dumdum-80d3-ed43230ee8ab            None

这是意料之中的。但是现在当我将其通过管道传输到 set-clipboard 并粘贴时 - 值为:

$hello | set-clipboard;
get-clipboard
None

这是我想要的实际值。不知何故,设置剪贴板的管道知道只拉出我最初要求的 属性 。即使是变量,也具有所有的属性。当我 运行 $hello.gettype() - 我看到的值为 Int32。如果 $hello 只是 return 我想要的值,这是有道理的,但它...不是。

但如果这还不够奇怪的话 - 我在调用命令中 运行 设置了一些函数,这只是一部分 - 所有函数 return 一个值 i '试图报告。所以:

$row = '' | select computername, ClusterNetworkRole, IP;
$row.computername = $name;
$row.clusternetworkrole = $hello;
$row.ip = dum.dum.dum.dum;
Return $row;

你知道 $row.clusternetworkrole 的输出是什么吗?大胆猜测。 每个 属性 除了我想要的那个。

$row
PSComputerName     : dumdum
RunspaceId         : b898bdad-dumdum-9eff-8a2beeefe78a
ClusterNetworkRole :
Computername       : dum
IP                 : dum.dum.dum.dum

它不仅提供了我不想要的确切属性 - 它实际上将这些属性添加为 $row 的成员。

$row.RunspaceID
b898bdad-dumdum-9eff-8a2beeefe78a

现在我可以通过在语句末尾附加“.value”来获得我想要的值,所以这不是一个需要解决的问题,而是一个到底是什么的问题powershell 正在做。它采取了这个简单、美丽的小声明 - 并对我的生活造成了严重破坏。

在您的特定情况下 枚举值的实例(System.Enum-派生类型):

  • 使用[int] $hello获取原始枚举(System.Enum-派生)值的数值,无需额外的NoteProperty 成员,例如远程处理基础结构添加的 PSComputerName(见下文)。

  • 使用 $hello.Value 获取枚举值的 string 表示(其符号 name 而不是比它的数量).

  • 如果您知道原始的 System.Enum 派生类型,并且该类型在您的本地会话中也可用,您可以 cast 反序列化的对象恢复到原来的类型;例如:
    [Microsoft.Foo.Bar.ClusterRole] $hello

  • $hello 在技术上是一个 [int],但装饰了额外的属性,并且有关 原始 类型的信息记录在隐藏文件中.pstypenames数组,反映了原始类型的继承层次,类型名称以Deserialized.为前缀;例如Deserialized.Microsoft.Foo.Bar.ClusterRole; PowerShell 的输出格式化系统导致通过(隐式应用)Format-Table 格式化此类对象,在本例中显示所有内容 实际 [int] 值 - 只有已显示 NoteProperty 个成员。

一般,您可以排除不需要的属性,如下所示:

  • 对于反序列化 type-faithfully 类型的对象,包括 strings。 NET 原始类型 (例如 [int][long]、...)加上 一些 (参见 MS-PSRP, PowerShell 远程处理协议规范),您可以访问 $hello.psobject.BaseObject 以获取没有任何 NoteProperty 成员的基础对象。

  • 对于其他人,您可以创建一个 new 对象(总是 [pscustomobject] 类型),通过管道连接到 Select-Object不需要的属性 已排除 ,如 Lee Dailey 所建议:

    • $hello | Select-Object * -Exclude PSComputerName, PSShowComputerName, RunspaceId

    • 或者,您可以专注于选择您想要的属性。

请继续阅读,了解为什么需要这样做。


PowerShell 的远程处理基础结构将以下属性添加到从远程调用返回的 每个 对象,作为 NoteProperty 成员:

  • PSComputerName ...远程计算机的名称

  • RunspaceId ...执行远程命令的运行空间的ID。

  • PSShowComputerName ... a hidden 属性 即,在通过返回的所有对象上设置为 $trueInvoke-Command-HideComputerName 开关,在默认输出中抑制 显示 PSComputerName 属性(但 属性仍然存在);如果将远程接收的对象通过管道传输到 Get-Member -Force.

  • ,则只能看到 PSShowComputerName 本身
  • 此外,System.Enum 派生类型作为 [int] 个实例返回,[string] 类型的 Value 属性 NoteProperty 添加了包含枚举值的 符号名称 .

  • 的成员

PSComputerNameRunspaceId 属性在一次针对 多台 台计算机的远程命令中很有用:假定接收输出的顺序是不能保证,这些属性会告诉您给定输出对象的来源。

PSShowComputerName 属性 允许您控制默认显示行为 - 但奇怪的是,它对是否显示 RunspaceId 没有影响。

System.Enum 派生类型的 Value 属性 补偿了通常发生在远程处理命令(和后台作业)中的类型保真度损失 - 只有一组有限的已知使用类型保真度反序列化类型 - 请参阅 .


虽然这些属性始终存在,但它们是否默认显示取决于返回对象的具体类型以及格式化数据与它们关联或由 PowerShell 默认应用。

此外,它们可能会在您明确地通过管道传输到 Format-* cmdlet 时以及在序列化期间显示,例如使用 ConvertTo-Json.

如果您不想要计算机名,我相信这会起作用:

(invoke-command $hi {Get-ClusterNetwork 'cluster backups'}).role