Immortal
Immortal

Reputation: 1180

How to compare two text files in power-shell and send the differences to an email

I have 2 text files that contains a list of SQL function names. I want to be able to compare a list in $A and $B. I then want a list of the SQL function names which are in $B and not in $A i.e. list $C to be send as an attachment to an email address.

Here is my code:

   $A = "c:\ReferenceFunctions.txt"
   $B = "c:\GeneratedFunctions.txt"
   $C = "c:\FileWithDifferences.txt"


    $fromaddress = "noreply@xyz"
    $toaddress = "123@hhh"
    $bccaddress = "[email protected]"
    $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);
} 

Variable $A has information as below:

fn_StatementEndOfMonth                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
fn_Performance_Sql                                                                                                                                                                                     
fn_DailyReturns 

Variable $B has the following info:

fn_Performance_Sql                                                                                                                                                                                     
fn_DailyReturns 
fn_SQLPerfomance_Monitor

$C Should then contain

fn_SQLPerfomance_Monitor

The problem with this code is that, it is sending an attachment with all the SQL Function names in both file $A and $B instead of the difference. I can't seem to figure out where am going wrong. Any one who can assist please? Been trying to figure out the whole day but no luck.

Upvotes: 2

Views: 571

Answers (4)

mklement0
mklement0

Reputation: 439597

You must filter the difference objects that Compare-Object outputs by their .SideIndicator property in order to only get the items that are unique to a given side (PSv3+ syntax):

Compare-Object (Get-Content $A) (Get-Content $B) -PassThru | 
  Where-Object Sideindicator -eq '=>' | 
    Out-File $C
  • Where-Object Sideindicator -eq '=>' outputs only those difference objects representing item that are unique to the right side (i.e., the second argument, the lines of file $B in this case).

    • A difference object is a [pscustomobject] instance that represents a single difference between the input collections. It has two properties:
      • .SideIndicator contains an arrow (string) that points toward the side that the input object at hand is unique to: <= or =>; if you use the -IncludeEqual switch, objects present in both collections are represented with ==
      • .InputObject represent the input object at hand.
  • -PassThru results in Compare-Object outputting the input objects themselves rather than the usual difference objects:

    • This enables you to send strings to the output file; without -PassThru, Out-File would write the difference objects as a whole, resulting in a table representation; you'd have to access the .InputObject property in order to get the string only.
    • However, a .SideIndicator property is still present, because PowerShell adds a NoteProperty member with that name to each object, so the Where-Object filter still works as intended.

Upvotes: 1

JosefZ
JosefZ

Reputation: 30173

The following code snippet shows how Compare-Object cmdlet works, and importance of SideIndicator property:

$a = @'
fn_StatementEndOfMonth
fn_Performance_Sql
fn_DailyReturns
'@ -split [environment]::NewLine               ### ReferenceFunctions
$b = @'
fn_Performance_Sql
fn_DailyReturns
fn_SQLPerfomance_Monitor
'@ -split [environment]::NewLine               ### GeneratedFunctions

[environment]::NewLine                         ### for better output readability
'Compare-Object $a $b'
Compare-Object $a $b
[environment]::NewLine
'Compare-Object $a $b | ? SideIndicator -ne ''<='''
Compare-Object -ReferenceObject $a -DifferenceObject $b| 
    Where-Object { $_.SideIndicator -ne '<=' }
[environment]::NewLine
'Compare-Object $a $b | ? SideIndicator -ne ''=>'''
Compare-Object -ReferenceObject $a -DifferenceObject $b | 
    Where-Object { $_.SideIndicator -ne '=>' }
[environment]::NewLine
'Compare-Object $a $b -PassThru'
Compare-Object $a $b -PassThru
[environment]::NewLine
'Compare-Object $a $b -PassThru | ? SideIndicator -ne ''<='''
Compare-Object -ReferenceObject $a -DifferenceObject $b -PassThru | 
    Where-Object { $_.SideIndicator -ne '<=' }
[environment]::NewLine
'Compare-Object $a $b -PassThru | ? SideIndicator -ne ''=>'''
Compare-Object -ReferenceObject $a -DifferenceObject $b -PassThru | 
    Where-Object { $_.SideIndicator -ne '=>' }

Output:

PS D:\PShell> D:\PShell\SO\50510142.ps1

Compare-Object $a $b

InputObject              SideIndicator
-----------              -------------
fn_SQLPerfomance_Monitor =>           
fn_StatementEndOfMonth   <=           

Compare-Object $a $b | ? SideIndicator -ne '<='
fn_SQLPerfomance_Monitor =>           

Compare-Object $a $b | ? SideIndicator -ne '=>'
fn_StatementEndOfMonth   <=           

Compare-Object $a $b -PassThru
fn_SQLPerfomance_Monitor
fn_StatementEndOfMonth

Compare-Object $a $b -PassThru | ? SideIndicator -ne '<='
fn_SQLPerfomance_Monitor

Compare-Object $a $b -PassThru | ? SideIndicator -ne '=>'
fn_StatementEndOfMonth

For those confused about Compare-Object output:

$x = Compare-Object $a $b
$x[0].Gettype()
$y = Compare-Object $a $b -PassThru
$y[0].Gettype()
IsPublic IsSerial Name                                     BaseType            
-------- -------- ----                                     --------            
True     False    PSCustomObject                           System.Object       
True     True     String                                   System.Object       

Above is valid despite of documentation (wrongly) says

Outputs

None, or the objects that are different.

When you use the PassThru parameter, Compare-Object returns the objects that differed. Otherwise, this cmdlet does not generate any output.

Upvotes: 0

Jacob Colvin
Jacob Colvin

Reputation: 2835

It seems like you actually want to do the exact opposite of compare-object. To get exactly what you said you needed in $C (I'm not sure why 'fn_StatementEndOfMonth' shouldn't be there as well), you can do the following:

$a = 'fn_StatementEndOfMonth
fn_Performance_Sql
fn_DailyReturns' -split '[\r\n]'

$b = 'fn_Performance_Sql
fn_DailyReturns
fn_SQLPerfomance_Monitor' -split '[\r\n]'

$c = $b | % { if($_ -notin $a){$_} }

$c

fn_SQLPerfomance_Monitor

Upvotes: 0

committhisgit
committhisgit

Reputation: 110

You're missing -PassThru switch on the Compare-Object command:

$A = Get-Content 'C:\temp\test1.txt'
$B = Get-Content 'C:\temp\test2.txt'
$C = 'c:\temp\diff.txt'

Compare-Object $A $B -PassThru | Out-File -FilePath $C

Upvotes: 0

Related Questions