将列添加到 Powershell 显示输出

Add Column to Powershell Display Output

我正在编写一个脚本来审计我们虚拟环境中的磁盘分区类型。我得到了产生以下输出的代码:

$vmName = "VM NAME"

$output = Invoke-VMScript -ScriptText {Get-Disk | select Number, @{name='Size (GB)';expr={[int]($_.Size/1GB)}}, PartitionStyle} -VM $vmName -GuestUser $Username -GuestPassword $Password

$output.ScriptOutput | FT -AutoSize

输出:

Number Size (GB) PartitionStyle
------- --------- --------------
      0       160 MBR

这本身就很好,但是我还想添加 VM 的名称,因为这将循环遍历数千个 VM。我试过了,但它为 ComputerName:

产生了空白输出
$vmName = "VM NAME"

$output = Invoke-VMScript -ScriptText {Get-Disk | select @{l="ComputerName";e={$vmName}}, Number, @{name='Size (GB)';expr={[int]($_.Size/1GB)}}, PartitionStyle} -VM $vmName -GuestUser $Username -GuestPassword $Password

$output.ScriptOutput | FT -AutoSize

输出:

ComputerName Number Size (GB) PartitionStyle
------------ ------ --------- --------------
                  0       160 MBR           

关于如何使这项工作有任何想法吗?

作为奖励,关于如何在其中添加 VM 的实际名称的任何想法,因为 vSphere 名称可能不是机器的实际名称?

tl;dr

  • Invoke-VMScript -ScriptText accepts only a string containing PowerShell commands, not a script block ({...}).

  • 由于没有单独的参数用于将 arguments 传递给 -ScriptText 代码,唯一的方法是包含 - 总是stringified - 来自 caller's 范围的值是使用 string 插值 ,如图所示在下一节中。

    • 值得注意的是,通常的基于脚本块的机制使用$using:范围来嵌入来自调用者的值工作。
  • 将脚本块文字 { ... } 传递给 -ScriptText 技术上 有效,因为在转换为字符串时 verbatim 使用了 {} 之间的内容,但是 最好避免,以避免概念混淆.


注:

  • 下面是对您的问题的解释以及对您原来的方法.

    的修复
  • 您可以绕过您的问题如果您添加计算列 通过 selectSelect-Object) on the caller's side (as hinted at by iRon), using the VM property of the object output by Invoke-VMScript, which contains the remote computer name (which Lee_Dailey 帮助发现):

    $vmName = "VM NAME"
    
    # Only run `Get-Disk` remotely, and run the `select` (`Select-Object`)
    # command *locally*:
    # Note the need to extract the actual script output via the .ScriptOutput
    # property and the use of the .VM property to get the computer name.
    $output = Invoke-VMScript -ScriptText { Get-Disk } -VM $vmName -GuestUser $Username -GuestPassword $Password |
      select -ExpandProperty ScriptOutput -Property VM |
        select @{l="ComputerName";e='VM'}, Number, @{name='Size (GB)';expr={[int]($_.Size/1GB)}}, PartitionStyle  
    

您通过 -ScriptText 传递给 Invoke-VMScript 的命令是在 另一台机器 上执行的,它对您的命令一无所知本地 $vmName 变量。

PowerShell remoting commands such as Invoke-Command 中,$using: 范围将是解决方案:... | select @{l="ComputerName";e={$using:vmName}}, ...

但是,$using:只能用于脚本块 ({ ... })。 当您 尝试 将脚本块传递给 Invoke-VMScript -ScriptText 时,它实际上在提交给 VM 之前转换为 字符串 , 因为 Invoke-VMScript-ScriptText 参数是 [string] 类型的 .

由于 Invoke-VMScript 没有用于从调用者范围传递参数的单独参数,从调用者范围包含值的唯一方法是使用 字符串插值:

$vmName = "VM NAME"

$output = Invoke-VMScript -ScriptText @"
  Get-Disk | 
    select @{ l="ComputerName"; e={ '$vmName' } }, 
           Number, 
           @{ name='Size (GB)'; expr={[int](`$_.Size/1GB)} },
           PartitionStyle
"@ -VM $vmName -GuestUser $Username -GuestPassword $Password
  • 注意这里使用了可扩展字符串 (@"<newline>...<newline>"@); 重要:结束分隔符 "@ 必须 在行的最开始 (甚至不允许在它之前有空格) ;有关 PowerShell 中字符串文字的更多信息,请参阅 this answer

  • $vmName 现在 在调用者的作用域中预先扩展 ;要使生成的 e={ ... } 脚本块在语法上有效,扩展的 $vmName 值必须包含在 '...'.

  • 相比之下,$_ 中的 $ 必须被 _escaped - 作为 `$- 因为它必须 通过 [=146] =]到远程机器。


就是说,因为您想确定远程计算机上的实际计算机名称,您甚至不需要字符串插值 ;使用 $env:COMPUTERNAME:

$vmName = "VM NAME"

$output = Invoke-VMScript -ScriptText @'
  Get-Disk | 
    select @{ l="ComputerName"; e={ $env:COMPUTERNAME } }, 
           Number, 
           @{ name='Size (GB)'; expr={[int]($_.Size/1GB)} },
           PartitionStyle
'@ -VM $vmName -GuestUser $Username -GuestPassword $Password

请注意,在这种情况下 - 因为不需要字符串插值 - 使用了 verbatim(文字)here-string (@'<newline>...<newline>'@),这也意味着不转义直通 $ 字符。是必需的。

当不需要字符串插值时,您可以实际通过脚本块传递脚本文本({ ... } ),这在一定程度上简化了语法(脚本块将其逐字字符串化为 {} 之间的内容),但是由于最终传递了 string,因此它是为了概念清晰,最好先使用一个。