首页 > 解决方案 > 如何将 cmdlet 的输出添加到数组中?

问题描述

我正在尝试确定我们的 Windows 服务器上是否安装了特定的 Windows 修补程序。我对 PowerShell 脚本很陌生,这就是我目前所拥有的:

$servers = Get-ADComputer -Filter {(OperatingSystem -like "Windows Server 2019*") -and (enabled -ne $false)} -Property *
$result = @()
ForEach ($item in $servers) {
    $testhotfix = Get-HotFix -Id KB4534310,KB4534314,KB4534283,KB4534288,KB4534297,KB4534309,KB4534271,KB4534273 -ComputerName $item.Name | `
    select $item.Name,$item.CanonicalName,$item.OperatingSystem
    $result += $testhotfix
}
$result | Export-Csv -Path C:\Users\user1\Desktop\Servers.csv -NoTypeInformation

创建的 CSV 文件包含一行包含我要查找的信息,后跟几行逗号,如下所示:

脚本输出

"SERVER1","somedomain.com/Servers/Non-Prod/New_Server_Staging/SERVER1","Windows Server 2019 Standard" ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,,

我们有几台服务器至少安装了一个修补程序。如何将每个服务器添加到$result数组?

谢谢

标签: arrayspowershell

解决方案


通常来说,一般来说:

select $item.Name,$item.CanonicalName,$item.OperatingSystem

应该:

select Name, CanonicalName, OperatingSystem

也就是说,您需要将属性名称(例如Name)而不是当前输入对象的属性(例如$item.Name传递给selectSelect-Objectcmdlet)。

最终效果是Select-Object创建自定义对象,其属性(错误地)以属性命名,并且它们本身没有 value,因为输入对象没有这样的属性。这解释了您看到的输出。

然而,更大的问题是即使这样也行不通,因为属性名称与对象相关,而不是$item与操作的对象输出的对象相关。Get-HotFixselect

事实证明,您真正需要的是使用Get-HotFix调用作为条件,以便仅在安装了至少一个指定的修补程序时为手头的计算机编写一个 CSV 行:

$hotfixIds = 'KB4534310', 'KB4534314', 'KB4534283', 'KB4534288', 'KB4534297', 'KB4534309', 'KB4534271', 'KB4534273'

if (0 -ne (Get-HotFix -ErrorAction SilentlyContinue -Id $hotfixIds -ComputerName $item.Name).Count)  {
  $result += $item | select Name, CanonicalName, OperatingSystem
}

笔记:

  • 请注意现在$item(手头的计算机)如何通过管道传输到select,以确保提取属性(以具有这些属性的自定义对象的形式)。

  • 您可以完全省略0 -eq并依赖 PowerShell 的隐式到布尔转换,其中任何非零数字的计算结果为(有关所有规则的摘要,请参阅此答案$true的底部部分。

    • 相反,如果您想测试所有正在安装的指定修补程序,请替换0 -ne$hotfixIds.Count -eq.
  • -ErrorAction SilentlyContinue消除未安装任何指定修补程序的计算机的错误;$Error您可以事后检查自动收集,或用于-ErrorVariable err收集变量中的所有命令特定错误$err

此外,您的整体命令可以大大简化 - 请参阅底部。


针对不同场景的解决方案,也可能令人感兴趣:

如果您想输出Get-HotFix对象的属性与对象的属性($item代表手头的计算机)结合起来:

以下命令:

  • 从输出对象中选择所有属性 ( )Get-HotFix-Property *
  • $item使用计算的属性从当前添加感兴趣的属性
# Additional 'KB...' values omitted for brevity.
Get-HotFix -Id KB4534310, KB4534314 -ComputerName $item.Name | 
  Select-Object -Exclude Name -Property *,
                  @{ n = 'Name'; e = { $item.Name } },
                  @{ n = 'CanonicalName'; e = { $item.CanonicalName } },
                  @{ n = 'OperatingSystem'; e = { $item.OperatingSystem } }

请注意,从输入对象(具有此类属性的输出对象,但它是-Exclude Name的)中排除该属性,因此可以将其添加为包含计算机名称的属性。NameGet-HotFixName


至于你尝试了什么:

除了Select-Object上面提到的属性名称问题之外,您的主要问题是您期望管道段作为条件,这不是管道的工作方式:

Get-HotFix ... | select ...

以上只是将Get-HotFix输出对象发送到select( Select-Object),然后它无条件地处理它们(并且,如前所述,在这些对象上查找具有给定名称的属性)。

现在,如果没有Get-HotFix产生任何输出,那么条件逻辑就会隐式应用:那么select命令就不会被调用。

相反,如果Get-HotFix产生多个输出,select在每个.

也就是说,如果我们天真地尝试从以下位置更正您的命令:

Get-HotFix ... | select ...

至:

Get-HotFix ... | ForEach-Object { $item | select ... }

您可能会在每台计算机上创建多个输出对象,即每当给定计算机碰巧安装了多个给定修补程序时。


您的(更正的)命令的简化版本:

您的命令可以简化为仅使用单个管道,而无需 aux。变量:

Get-ADComputer -Filter '(OperatingSystem -like "Windows Server 2019*") -and (enabled -ne $false)' -Property * |
  ForEach-Object {
    if (0 -ne (Get-HotFix -ErrorAction SilentlyContinue -ComputerName $item.Name -Id KB4534310,KB4534314,KB4534283,KB4534288,KB4534297,KB4534309,KB4534271,KB4534273).Count) {
      $item | select Name, CanonicalName, OperatingSystem
    }
  } | Export-Csv -Path C:\Users\user1\Desktop\Servers.csv -NoTypeInformation

笔记:

  • 如果以结束一行|,则不需要尾随`信号线延续。

    • PowerShell [Core] v7.0+ 现在还允许放置在下一行|的开头。
  • 使用单引号字符串 ( '...') 代替脚本块 ( { ... }) 来传递-Filter参数,因为最好避免使用脚本块 ( { ... }) 作为-Filter参数

  • 使用创建的输出自定义对象实例$item | select Name, CanonicalName, OperatingSystem直接发送到管道。


推荐阅读