PowerShell对比文本文件差异并邮件发送的代码异常排查
问题描述
我有两个存储SQL函数名称列表的文本文件,想要对比$A与$B的内容,筛选出仅存在于$B而不存在于$A的SQL函数名称并写入$C文件,再将$C作为附件发送至指定邮箱。
我的代码如下:
$A = "c:\ReferenceFunctions.txt" $B = "c:\GeneratedFunctions.txt" $C = "c:\FileWithDifferences.txt" $fromaddress = "noreply@xyz" $toaddress = "123@hhh" $bccaddress = "3333@nnn.com" $login = "yourlogin" $password = "password" | Convertto-SecureString -AsPlainText -Force $smtpserver = "gggg.com" $smtp = new-object Net.Mail.SmtpClient($smtpserver, 800) $smtp.EnableSsl = $true $smtp.Credentials = New-Object System.Net.NetworkCredential($login, $password); $body = "see attached " $Subject = "checking differences" $message = new-object System.Net.Mail.MailMessage $message.From = $fromaddress $message.To.Add($toaddress) #$message.CC.Add($CCaddress) $message.Bcc.Add($bccaddress) $message.IsBodyHtml = $True $message.Subject = $Subject $message.body = $body $message.Priority = [System.Net.Mail.MailPriority]::High Compare-object (Get-Content $A) (Get-Content $B) | Out-File $C #Attach the eported file with differences $attachment = $C $attach = new-object Net.Mail.Attachment($attachment) $message.Attachments.Add($attach) IF (Compare-Object -ReferenceObject (Get-Content $A) -DifferenceObject (Get-Content $B)){ $smtp.Send($message); }
$A文件内容:
fn_StatementEndOfMonth fn_Performance_Sql fn_DailyReturns
$B文件内容:
fn_Performance_Sql fn_DailyReturns fn_SQLPerfomance_Monitor
预期$C文件应包含:
fn_SQLPerfomance_Monitor
但当前代码执行后,附件中包含了两个文件中的所有SQL函数名称,而非预期的差异内容,我无法定位问题所在,请求协助排查。
问题排查与修复方案
我帮你梳理了代码里的问题,核心有两个关键点:
问题根源
- 文件内容读取方式错误:你的函数名是在同一行用空格分隔的,
Get-Content默认会把整行内容当成一个字符串项,而不是拆分单个函数名。这会导致Compare-Object对比的是两行完整内容,自然会认为两边都是差异项,输出所有内容。 Compare-Object默认行为不符合需求:默认情况下,它会返回两边所有差异项(包括仅在$A的<=标记项和仅在$B的=>标记项),且输出包含额外格式,直接写入文件会出现不符合预期的内容。
分步修复
1. 拆分文件中的函数名
先把单行空格分隔的内容拆分成单个函数名数组,确保对比逻辑针对每个函数:
$contentA = (Get-Content $A) -split '\s+' | Where-Object { $_ -ne '' } $contentB = (Get-Content $B) -split '\s+' | Where-Object { $_ -ne '' }
用-split '\s+'按任意空白字符拆分,再过滤空字符串避免无效项。
2. 筛选仅存在于$B的函数
对比拆分后的数组,筛选出SideIndicator为=>的项(仅属于$B的函数),提取纯函数名:
$onlyInB = Compare-Object -ReferenceObject $contentA -DifferenceObject $contentB | Where-Object { $_.SideIndicator -eq '=>' } | Select-Object -ExpandProperty InputObject
3. 写入结果文件
把筛选后的结果写入$C,确保每行一个函数名:
$onlyInB | Out-File $C -Encoding utf8
4. 优化邮件发送逻辑
检查是否有差异内容,避免发送空附件的邮件,同时清理资源防止内存泄漏:
if ($onlyInB) { $attach = New-Object Net.Mail.Attachment($C) $message.Attachments.Add($attach) $smtp.Send($message) $attach.Dispose() } $smtp.Dispose()
修复后的完整代码
$A = "c:\ReferenceFunctions.txt" $B = "c:\GeneratedFunctions.txt" $C = "c:\FileWithDifferences.txt" $fromaddress = "noreply@xyz" $toaddress = "123@hhh" $bccaddress = "3333@nnn.com" $login = "yourlogin" $password = "password" | Convertto-SecureString -AsPlainText -Force $smtpserver = "gggg.com" $smtp = New-Object Net.Mail.SmtpClient($smtpserver, 800) $smtp.EnableSsl = $true $smtp.Credentials = New-Object System.Net.NetworkCredential($login, $password); $body = "see attached " $Subject = "checking differences" $message = New-Object System.Net.Mail.MailMessage $message.From = $fromaddress $message.To.Add($toaddress) #$message.CC.Add($CCaddress) $message.Bcc.Add($bccaddress) $message.IsBodyHtml = $True $message.Subject = $Subject $message.body = $body $message.Priority = [System.Net.Mail.MailPriority]::High # 修复:拆分函数名并筛选仅在B中的项 $contentA = (Get-Content $A) -split '\s+' | Where-Object { $_ -ne '' } $contentB = (Get-Content $B) -split '\s+' | Where-Object { $_ -ne '' } $onlyInB = Compare-Object -ReferenceObject $contentA -DifferenceObject $contentB | Where-Object { $_.SideIndicator -eq '=>' } | Select-Object -ExpandProperty InputObject # 写入结果文件 $onlyInB | Out-File $C -Encoding utf8 # 仅当有差异时发送邮件 if ($onlyInB) { $attach = New-Object Net.Mail.Attachment($C) $message.Attachments.Add($attach) $smtp.Send($message) $attach.Dispose() } # 清理SMTP客户端资源 $smtp.Dispose()
这样修改后,$C文件就会只包含fn_SQLPerfomance_Monitor,完全符合你的预期啦。
内容的提问来源于stack exchange,提问作者Immortal
相关产品推荐
相关产品推荐

