【问题标题】:Add Column to Powershell Display Output将列添加到 Powershell 显示输出
【发布时间】:2020-12-11 13:15:53
【问题描述】:

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

$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           

关于如何完成这项工作的任何想法?

作为奖励,关于如何在其中添加虚拟机的实际名称,因为 vSphere 名称可能不是机器的实际名称,有什么想法吗?

【问题讨论】:

  • FT -AutoSize -> FT *,@{Name='VMName';Expression={$vmName}} -AutoSize
  • @MathiasR.Jessen 我把最后一行改成了这个,但输出根本没有改变(仍然和输出 #1 一样):“$output.ScriptOutput | FT *,@{ Name='VMName';Expression={$vmName}} -AutoSize"
  • @iRon 也没有运气,仍然与输出 1 相同
  • 您确定目标系统尚未添加到您返回的信息中吗? Invoke-Command cmdlet 添加了包含远程系统名称的 .PSComputerName 属性。它通常是隐藏的......你的Invoke-VMScript 可能正在做类似的事情。
  • @Lee_Dailey Invoke-VMScript 返回列“VM”、“ExitCode”和“ScriptOutput”。 VM 有 VM 的名称,这很棒。现在需要弄清楚如何将它与 ScriptOutput 结合起来

标签: powershell powercli


【解决方案1】:

tl;dr

  • Invoke-VMScript -ScriptText 仅接受包含 PowerShell 命令的字符串不接受script block ({...})。

  • 由于没有单独的参数将 arguments 传递给 -ScriptText 代码,唯一的方法是包含 - 总是 stringified - 来自 调用者的范围是使用字符串插值,如下一节所示。

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


注意:

  • 以下是对您的问题的解释以及对您的原始方法的修复。

  • 如果您通过select (Select-Object) 添加计算列,您可以绕过您的问题在调用方(如iRon 所暗示),使用Invoke-VMScript 输出的对象的VM 属性,其中包含远程计算机名称(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 命令(例如Invoke-Command)中,$using: 范围将是解决方案:... | select @{l="ComputerName";e={$using:vmName}}, ...

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

由于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 值必须包含在'...' 中。

  • 相比之下,$_ 中的$ 必须被_转义为`$- 因为它必须传递到远程机器。


也就是说,因为您想确定该远程计算机上的实际计算机名称​​,您甚至不需要字符串插值;使用$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>'@)被使用,这也意味着没有转义传递@ 987654369@ 字符。是必需的。

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

【讨论】:

  • 效果很好,谢谢!我是 Powershell 新手,所以这绝对是一次学习体验。
  • 非常感谢。附带问题 - 知道如何从该 ScriptOutput 中选择特定列吗?
  • @Daniel,@iRon 的评论显示了一个例子:Select @{name='Size (GB)';expr={[int]($_.ScriptOutput.Size/1GB)}}, ...。对于计算出来的属性很好,但是如果你只是想要一个属性原样而不重命名它,比如.ScriptOutput.Number,这会很麻烦;这就是为什么我的问题绕过解决方案首先在中间步骤中提取 ScriptOutput 值的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-25
  • 2013-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-12
相关资源
最近更新 更多