You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell中快速对比两数组找重复值并保留原结构问题

PowerShell高效对比AD与Excel服务器列表并保留完整对象结构

问题需求

需要在PowerShell中完成以下操作:

  • 从Active Directory(AD)获取服务器名称列表
  • 导入含多属性(ServerName、TestServer、DMZServer、PatchWindow、ASA等)的Excel服务器数据
  • 找出同时存在于AD和Excel中的服务器,并且保留Excel数据的原有对象结构
  • 之前尝试LINQ的Intersect方法返回空结果,使用foreach循环逐个校验效率过低,寻求高效方案

问题分析

  1. LINQ Intersect失效原因:
    • AD获取的服务器名是纯主机名(如SERVER01),而Excel处理后的部分服务器名添加了.dmz.com后缀,两者字符串不匹配
    • 即使字符串匹配,Intersect仅返回匹配的字符串值,无法保留Excel中的完整对象属性
  2. foreach循环效率低:逐个遍历对比的时间复杂度为O(n*m),数据量较大时性能极差

高效解决方案

使用**哈希集合(HashSet)**存储AD服务器名,利用其O(1)的查找效率,快速过滤Excel中的匹配对象:

优化后的完整脚本

CLS
$ErrorActionPreference = 'SilentlyContinue'
$ErrorFile = Join-Path -Path (Split-Path $script:MyInvocation.MyCommand.Path) -ChildPath "ERROR.txt"

# 配置TLS1.2并确保ImportExcel模块可用
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
if (-not (Get-Module -ListAvailable -Name "ImportExcel")) {
    Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
    Install-Module -Name ImportExcel -Scope CurrentUser -Force -Confirm:$False
}
Import-Module ImportExcel -Force

CLS
Get-Date
Write-Host " "

# 全局变量
$Path = Split-Path $script:MyInvocation.MyCommand.Path

# 读取Excel数据并转换为自定义对象
$ExcelFile = (Get-ChildItem -Path "$Path\*.xlsx").FullName
$worksheet = (Open-ExcelPackage -Path $ExcelFile).Workbook.Worksheets[1].Name
$Servers = Import-Excel -Path $ExcelFile -WorkSheetname $worksheet -StartRow 1

# 转换Excel数据结构并去重
$ServersList = $Servers | ForEach-Object {
    [pscustomobject]@{ 
        'ServerName'   = $_.Child
        'TestServer'   = $_.'Test server'
        'DMZServer'    = $_.DMZ
        'PatchWindow'  = $_.'Patch Window'
        'ASA'          = $_.Primary 
    }
} | Sort-Object -Property ServerName -Unique

# 处理DMZ服务器后缀
$FilteredServers = $ServersList | ForEach-Object {
    if ($_.DMZServer -eq $true) {
        $_.ServerName += ".dmz.com"
    }
    $_
}

# 从AD获取启用的服务器名称,存入HashSet(高效查找)
$searcher = [adsisearcher]::new()
$searcher.Sort.PropertyName = "name"
$searcher.SearchRoot = [adsi]"LDAP://OU=Servers,DC=blah,DC=com"
$searcher.PageSize = 100
$searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
$searcher.PropertiesToLoad.AddRange(('name','distinguishedname'))

# 创建HashSet存储AD服务器名(忽略大小写)
$ADServerNames = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($Computer in $searcher.FindAll()) {
    $null = $ADServerNames.Add($Computer.Properties.name[0])
}

# 高效筛选同时存在于AD和Excel中的服务器对象
$MatchingServers = $FilteredServers | Where-Object { $ADServerNames.Contains($_.ServerName) }

# 输出结果或存入变量使用
$MatchingServers

关键优化点

  1. 哈希集合存储AD服务器名:
    • 使用HashSet<string>存储AD服务器名,查找操作的时间复杂度为O(1),远快于数组遍历
    • 指定OrdinalIgnoreCase忽略大小写,避免因大小写差异导致匹配失败
  2. 管道化处理数据:
    • 替换原脚本中+=数组拼接的方式,改用管道ForEach-Object生成对象,避免频繁创建新数组带来的性能损耗
  3. 直接过滤完整对象:
    • 通过Where-Object直接筛选FilteredServers中ServerName存在于哈希集合的对象,直接保留完整的Excel属性结构

验证说明

  • 若AD中的服务器名是纯主机名,而Excel处理后添加了后缀(如.dmz.com),需确保AD中是否存在带后缀的名称,或调整逻辑(如去掉Excel中的后缀再对比)
  • 哈希集合的忽略大小写配置可根据实际环境调整,若环境严格区分大小写则移除[System.StringComparer]::OrdinalIgnoreCase参数

内容的提问来源于stack exchange,提问作者Koobah84

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:53:22