How to compare 2 text files using powershell - powershell

I want to be able compare the contents of 2 text files and see if the contents of those files are the same. If the contents of the 2 files are different, I need this powershell script to send me an email with a list of differences from the 2 files. Here is my code:
$fromaddress = "noreply#xy.com"
$toaddress = "me#xy.com "
$Subject = "Comparing 2 text files"
$login = "abc"
$password = "12345" | Convertto-SecureString -AsPlainText -Force
$smtpserver = "smtp.office.com"
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$message.Priority = [System.Net.Mail.MailPriority]::High
$smtp = new-object Net.Mail.SmtpClient($smtpserver, 587)
$smtp.EnableSsl = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($login, $password);
IF (Compare-Object -ReferenceObject (Get-Content $dir\file1.txt) -DifferenceObject (Get-Content $dir\file2.txt)){
Write-Output "The files are different "
$smtp.Send($message);
}
Else {
Write-Output "The files are not different"
}
I have looked at different online resources that suggested the use of Compare-Object cmdlet but it doesn't seem to work in my situation. The problem with my code is that, its returning everything from file1. Any one who knows why this is not working for me?

To compare two file and determine if they are identical use a file hash:
$hash1 = Get-FileHash $dir\file1.txt
$hash2 = Get-FileHash $dir\file2.txt
if($hash1 -eq $hash2){
'They are the same'
}else{
'They are NOT the same
}

This scans the 2 text files for differences and pipes it to a third File
$File1 = "C:\Scripts\Txt1.txt"
$File2 = "C:\Scripts\Txt2.txt"
$Location = "C:\Scripts\Txt3.txt"
compare-object (get-content $File1) (get-content $File2) | format-list | Out-File $Location
Output:

You're almost right with your initial script. You just need to add -IncludeEqual
See below. Text files 1 & 2 are just "hi there." Text file 3 is "hi there2"
$1 = gc 'H:\Skype Migrations\file1.txt'
$2 = gc 'H:\Skype Migrations\file2.txt'
$3 = gc 'H:\Skype Migrations\file3.txt'
IF ( ( Compare-Object -ReferenceObject $1 -DifferenceObject $3 -IncludeEqual ).sideindicator -ne "==" )
{ Write-Output "The files are different " }
Else
{ Write-Output "The files are not different" }
Edited to showcase how to use the IncludeEqual

Related

How to look for a string in a filename and send that file via email?

I have a folder that contains several PDF files. They have a unique identifier within the filename that is two characters. Example: XYZ_A1_123.pdf, XYZ_QQ_456.pdf, etc. A1 and QQ are the identifiers.
The identifiers match specific email addresses. I have a CSV file that has two columns, the ID and the matching email address. I can't get my script to look for that specific identifier and send the file. If the PDF file has only the identifier in the name, such as "A1.pdf", the script works fine. Here's my code so far. Also, I'm looking to add a progress bar but not sure how. Any help is appreciated. Thanks!
$csv = Import-Csv "," -path C:\Test\emails.csv -header "id","email"
foreach ($item in $csv) {
$filename = "$($item.id).pdf"
}
$emailSmtpServer = "smtp.gmail.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "email#address.com"
$emailSmtpPass = "mypassword"
$emailFrom = "me#address.com"
$emailTo = $item.email
$emailMessage = New-Object System.Net.Mail.MailMessage( $emailFrom , $emailTo )
$emailMessage.Subject = "This is the subject"
#$emailMessage.IsBodyHtml = $true #true or false depends
$emailMessage.Body = "This is the body."
$emailMessage.Attachment = $filename
$emailMessage.Attachments.add($filename)
$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer ,
$emailSmtpServerPort )
$SMTPClient.EnableSsl = $True
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(
$emailSmtpUser , $emailSmtpPass );
$SMTPClient.Send( $emailMessage )
Creating a test file to mimic the file you described with codes and e-mail addresses:
echo "AF,a#p.com" > test.csv
echo "QC,q#p.com" >> test.csv
cat .\test.csv
The following snippet will pull the file's code, compare it to the address list, and retrieve the e-mail address if present:
$addresses = Import-Csv -Path .\test.csv -Header code, address
$files = ls .\*.pdf
foreach ($file in $files) {
$file_code = $file.name.Split('_')[1]
$addresses | where { $_.code -eq $file_code } | select address
# ...send email...
}
Looks like you've got the e-mail stuff on-lock so I won't rehash that here. I'd only do the e-mail stuff if you find a matching address.

Power Shell: If condition variable comparison not working

I have a piece of code comparing two values, and if the condition is satisfied it sends out an email. But it is not working, help is appreciated.
code:
$filesize = Get-ChildItem $filename | Select-Object Length | Format-Wide
$filesize
$num=1265
$num
if("$filesize" -gt "$num")
{
$SMTPServer = "10.20.19.94"
$SMTPPort = 25
$username = "vcenter#somosadc.com"
#Define the receiver of the report
$to = "jeevan.m2#hcl.com"
$subject = "VM Snapshot Report"
$body = "VM Snapshot Report"
$attachment = new-object Net.Mail.Attachment($filename)
$message = New-Object System.Net.Mail.MailMessage
$message.subject = $subject
$message.body = $body
$message.to.add($to)
$message.from = $username
$message.attachments.add($attachment)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $false
#$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
write-host "Mail Sent"
}
output:
1262
1265
Mail Sent
Why is it sending email if $filesize=1262 is less than $num=1265. It is killing me.
Because you're not comparing two numbers, you're comparing two strings.
Remove the Format-Wide command from the first pipeline, and remove the quotes around the arguments in your if condition:
$filesize = Get-ChildItem $filename | Select-Object Length
$num = 1265
if($filesize.Length -gt $num) {
<# ... #>
}

Emailing a Hard Drive Disk Space Alert Using Powershell

I've been browsing the web trying to find a way if possible to email a low disk space alert from a Gmail account to a shared mail box using power shell but Im struggling with a query I've managed to piece together.
$EmailFrom = "FromEmail#Gmail.com"
$EmailTo = "ToEmail#Gmail.com"
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("Username", "Password");
$Computers = "Local Computer"
$Subject = "Disk Space Storage Report"
$Body = "This report was generated because the drive(s) listed below have less than $thresholdspace % free space. Drives above this threshold will not be listed."
[decimal]$thresholdspace = 50
$tableFragment = Get-WMIObject -ComputerName $computers Win32_LogicalDisk `
| select __SERVER, DriveType, VolumeName, Name, #{n='Size (Gb)' ;e={"{0:n2}" -f ($_.size/1gb)}},#{n='FreeSpace (Gb)';e={"{0:n2}" -f ($_.freespace/1gb)}}, #{n='PercentFree';e={"{0:n2}" -f ($_.freespace/$_.size*100)}} `
| Where-Object {$_.DriveType -eq 3} `
| ConvertTo-HTML -fragment
$regexsubject = $Body
$regex = [regex] '(?im)<td>'
if ($regex.IsMatch($regexsubject)) {$smtpclinet.send($fromemail, $EmailTo, $Subject, $Body)}
Script runs but nothing happens, any help would be fantastic!!!
My version would be longer because I'd have made a substitute for Send-MailMessage such that swapping between mine and Send-MailMessage is trivial.
This is one possible way of doing it. There are good uses for the Fragment parameter on ConvertTo-Html, but not much of a justification to do so here.
This is a script and expected to be a .ps1 file. Mandatory things I don't really want to hard-code beyond a default are set in the param block.
param(
[String[]]$ComputerName = $env:COMPUTERNAME,
[Decimal]$Theshold = 0.5,
[PSCredential]$Credential = (Get-Credential)
)
#
# Supporting functions
#
# This function acts in much the same way as Send-MailMessage.
function Send-SmtpMessage {
param(
[Parameter(Mandatory = $true, Position = 1)]
[String[]]$To,
[Parameter(Mandatory = $true, Position = 2)]
[String]$Subject,
[String]$Body,
[Switch]$BodyAsHtml,
[String]$SmtpServer = $PSEmailServer,
[Int32]$Port,
[Switch]$UseSSL,
[PSCredential]$Credential,
[Parameter(Mandatory = $true)]
[String]$From
)
if ([String]::IsNullOrEmtpy($_)) {
# I'd use $pscmdlet.ThrowTerminatingError for this normally
throw 'A value must be provided for SmtpServer'
}
# Create a mail message
$mailMessage = New-Object System.Net.Mail.MailMessage
# Email address formatting si validated by this, allowing failure to kill the command
try {
foreach ($recipient in $To) {
$mailMessage.To.Add($To)
}
$mailMessage.From = $From
} catch {
$pscmdlet.ThrowTerminatingError($_)
}
$mailMessage.Subject = $Subject
$mailMessage.Body = $Body
if ($BodyAsHtml) {
$mailMessage.IsBodyHtml = $true
}
try {
$smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port)
if ($UseSSL) {
$smtpClient.EnableSsl = $true
}
if ($psboundparameters.ContainsKey('Credential')) {
$smtpClient.Credentials = $Credential.GetNetworkCredential()
}
$smtpClient.Send($mailMessage)
} catch {
# Return errors as non-terminating
Write-Error -ErrorRecord $_
}
}
#
# Main
#
# This is inserted before the table generated by the script
$PreContent = 'This report was generated because the drive(s) listed below have less than {0} free space. Drives above this threshold will not be listed.' -f ('{0:P2}' -f $Threshold)
# This is a result counter, it'll be incremented for each result which passes the threshold
$i = 0
# Generate the message body. There's not as much error control around WMI as I'd normally like.
$Body = Get-WmiObject Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $ComputerName | ForEach-Object {
# PSCustomObject requires PS 3 or greater.
# Using Math.Round below means we can still perform numeric comparisons
# Percent free remains as a decimal until the end. Programs like Excel expect percentages as a decimal (0 to 1).
[PSCustomObject]#{
ComputerName = $_.__SERVER
DriveType = $_.DriveType
VolumeName = $_.VolumeName
Name = $_.Name
'Size (GB)' = [Math]::Round(($_.Size / 1GB), 2)
'FreeSpace (GB)' = [Math]::Round(($_.FreeSpace / 1GB), 2)
PercentFree = [Math]::Round(($_.FreeSpace / $_.Size), 2)
}
} | Where-Object {
if ($_.PercentFree -lt $Threshold) {
$true
$i++
}
} | ForEach-Object {
# Make Percentage friendly. P2 adds % for us.
$_.PercentFree = '{0:P2}' -f $_.PercentFree
$_
} | ConvertTo-Html -PreContent $PreContent | Out-String
# If there's one or more warning to send.
if ($i -gt 0) {
$params = #{
To = "ToEmail#Gmail.com"
From = "FromEmail#Gmail.com"
Subject = "Disk Space Storage Report"
Body = $Body
SmtpServer = "smtp.gmail.com"
Port = 587
UseSsl = $true
Credential = $Credential
}
Send-SmtpMessage #params
}

Powershell - Email does not have same format as text despite Out-string

Folks,
Googling shows me lots of folks have this problem, however the answers I'm getting do not seem to work for me. Either that, or I don't understand.
Situation: I have a script that polls and gives a file count. It works great and I pipe it to a text file
Foreach ($Directory in $Directories) {
Write-Output "You have $Results files in that folder" | Out-File "C:\Filecheck.txt" -Append
}
Filecheck looks great. It does the above loop 6 times (as I have 6 directories) and it does the carriage returns.
In email, its all jumbled up. On here, someone suggested I use the out-string, so Ive done this:
$body = GC "C:\Filecheck.txt" | Out-string
I've also seen
$body = GC "C:\Filecheck.txt" -Raw
I get the email fine, but again, its still all one line, with no carriage returns.
Anyone have any idea? I know Im so close.
You could try using the [Environment] newline. I tested with the code below and the e-mail looked good and with the correct line breaks:
$DirectoriesFiles = 2,3,4,5
$newline = [Environment]::NewLine
$body = "List of number of files" + $newline
Foreach ($numOfFiles in $DirectoriesFiles) {
$body += "You have $numOfFiles files in that folder" + $newline
}
$ol = New-Object -comObject Outlook.Application
$Mail = $ol.CreateItem(0)
$Mail.To = "someone"
$Mail.Subject = "some test e-mail"
$Mail.Body = $body
$Mail.save() #or send
For the example's sake I just assumed you have an array with the number of files in the folder, but I think you can understand how to adapt to your context from here. My resulting e-mail looked like this:
List of number of files
You have 2 files in that folder
You have 3 files in that folder
You have 4 files in that folder
You have 5 files in that folder
Thanks for your help. My company email didn't like the format, but I format in html (using ) and utilize IsBodyHTML tag, it works like a charm!
Del "D:\Filecheck.txt"
$Directories = GC "D:\Directory.txt"
Foreach ($Directory in $Directories) {
$Results = (Get-ChildItem $Directory).count
If ($Results -gt 0) {
Write-Output "...You have $Results files stuck in $Directory...<br><br> " | Out-File "D:\Filecheck.txt" -Append
} else {
Write-Output "Phew! We're good, <br><br>" | Out-File "D:\Filecheck.txt" -Append
}
$Results = $null
}
$body = GC "D:\Filecheck.txt"
Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin -erroraction silentlyContinue
$SmtpClient = new-object system.net.mail.smtpClient
$SmtpServer = "localhost"
$SmtpClient.host = "relay.me.local"
$msg = new-object Net.Mail.MailMessage
$msg.IsBodyHTML = $true
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = "TelluRyesFileCheck#me.you"
$msg.To.Add("me#you.org")
$msg.Subject = "Checking if files exist on 9901/2"
$msg.Body = $body
$SmtpClient.Send($msg)

PowerShell - Send-MailMessage - Export mail to a file

I'm using the Send-MailMessgae Cmdlet to send an automated message with content each quarter.
The actual email works as expected etc however I want to save a copy of this email to a network share.
I've tried using Tee-Object to place it in a variable and then out-file to save it but this did not work.
I'm I'm thinking the only way i'm going to be able to do this is to just create a Here String with the details in it them output that.
Ideally I would like it to be in the .eml format as its a conformation that we are notifying users for the project the email is for.
I hope this makes sense.
Kind Regards,
Nigel Tatschner
You could actually use Tee-Object to send the data straight to the file, instead of trying to use both Out-File and Tee-Object.
$logfile = 'C:\data\logfiles'
<data> | Tee-Object $logfile -Append
You can use System.Net.Mail.MailMessage
# Based on MailMessageExt
# By Allan Eagle | 11 Jan 2009
# http://www.codeproject.com/KB/IP/smtpclientext.aspx
######################################
function createMailMessage([string]$from, [string]$to, [string]$subject) {
[System.Net.Mail.MailMessage]$msg = New-Object -TypeName "System.Net.Mail.MailMessage" -ArgumentList $from, $to
[System.Text.Encoding]$enc = [System.Text.Encoding]::UTF8
$msg.SubjectEncoding = $enc
$msg.Subject = $subject
$msg.BodyEncoding = $enc
return $msg
}
######################################
function mailAddAttachment([System.Net.Mail.MailMessage]$msg, [string]$filePath) {
$fileExists = Test-Path -path $filePath -pathtype leaf
if ($fileExists) {
$fileName = Split-Path $filePath -Leaf
[System.Net.Mail.Attachment]$att = New-Object -TypeName "System.Net.Mail.Attachment" -ArgumentList $filePath
$att.ContentType.MediaType = [System.Net.Mime.MediaTypeNames+Application]::Octet
$att.ContentType.Name = $fileName
$att.ContentDisposition.FileName = $fileName
$att.ContentDisposition.DispositionType = [System.Net.Mime.DispositionTypeNames]::Attachment
$att.ContentDisposition.Inline = $false
$att.TransferEncoding = [System.Net.Mime.TransferEncoding]::Base64
$msg.Attachments.Add($att)
$att = $null
return $true
} else {
$txt = "File ""$filePath"" not found"
Write-Output $txt
return $false
}
}
######################################
function saveMailMessage([System.Net.Mail.MailMessage]$msg, [string]$filePath) {
$binding = [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic
$mailMessageType = $msg.GetType()
$emlFilePath = $filePath
[System.Type]$scType = [System.Type]::GetType("System.Net.Mail.SmtpClient")
[System.Net.Mail.SmtpClient]$smtpClient = New-Object -TypeName "System.Net.Mail.SmtpClient"
$scType = $smtpClient.GetType()
[System.Type]$booleanType = [System.Type]::GetType("System.Boolean")
[System.Reflection.Assembly]$assembly = $scType.Assembly
[System.Type]$mailWriterType = $assembly.GetType("System.Net.Mail.MailWriter")
[System.IO.FileStream]$fileStream = New-Object -TypeName "System.IO.FileStream" -ArgumentList ($emlFilePath,[System.IO.FileMode]::Create)
[System.Array]$typeArray = ([System.Type]::GetType("System.IO.Stream"))
[System.Reflection.ConstructorInfo]$mailWriterConstructor = $mailWriterType.GetConstructor($binding ,$null, $typeArray, $null)
[System.Array]$paramArray = ($fileStream)
$mailWriter = $mailWriterConstructor.Invoke($paramArray)
#get MailMessage.Send(MailWriter,Boolean,Boolean)
$doubleBool = $true
[System.Array]$typeArray = ($mailWriter.GetType(),$booleanType,$booleanType)
[System.Reflection.MethodInfo]$sendMethod = $mailMessageType.GetMethod("Send", $binding, $null, $typeArray, $null)
if ($null -eq $sendMethod) {
$doubleBool = $false
[System.Array]$typeArray = ($mailWriter.GetType(),$booleanType)
[System.Reflection.MethodInfo]$sendMethod = $mailMessageType.GetMethod("Send", $binding, $null, $typeArray, $null)
}
#get MailWriter.Close()
[System.Array]$typeArray = #()
[System.Reflection.MethodInfo]$closeMethod = $mailWriterType.GetMethod("Close", $binding, $null, $typeArray, $null)
#execute MailMessage.Send(MailWriter,Boolean,Boolean)
[System.Array]$sendParams = ($mailWriter,$true)
if ($doubleBool) {
[System.Array]$sendParams = ($mailWriter,$true,$true)
}
$sendMethod.Invoke($msg,$binding,$null,$sendParams,$null)
#execute MailWriter.Close()
[System.Array]$closeParams = #()
$closeMethod.Invoke($mailWriter,$binding,$null,$closeParams,$null)
}
######################################
function Get-ScriptDirectory {
Split-Path -Parent $PSCommandPath
}
######################################
$smtpServer = "smtp.here.com"
$smtpPort = 25
######################################
#$fileDir = Convert-Path "."
$fileDir = Get-ScriptDirectory
$fileDir
[System.Text.Encoding]$enc = [System.Text.Encoding]::UTF8
$dt = Get-Date
$dtStart = Get-Date $dt -Hour 0 -Minute 0 -Second 0
$baseName = $fileDir+"\report_"+$dtStart.toString("yyyy-MM-dd")
$repName = $baseName+".csv"
$emlName = $baseName+".eml"
[System.Collections.ArrayList]$rep = #()
$rep.Add("This is a report")
$rep.Add("----------------")
for ($i=0; $i -lt 100; $i++) {
$index = $rep.Add("Line $i")
}
Set-Content $repName $rep
$from = "Sender <Me#here.com>"
$to = "Recipient <Them#there.com>"
$subject = "This is a report generated here on "+$dtStart.toString("yyyy-MM-dd")
$body = "$subject`n`n$repName"
[System.Net.Mail.MailMessage]$msg = createMailMessage $from $to $subject
$msg.IsBodyHtml = $false
$msg.Body = $body
mailAddAttachment $msg $repName
$emlName
[System.Net.Mail.SmtpClient]$smtp = New-Object -TypeName "System.Net.Mail.SmtpClient" -ArgumentList $smtpServer, $smtpPort
# $smtp.Send($msg)
saveMailMessage $msg $emlName
$msg.Dispose()
Remove-Item $repName
Pause