I'm trying to compare two files and if their content matches I want it to preform the tasks in the if statement in Powershell 4.0
Here is the gist of what I have:
$old = Get-Content .\Old.txt
$new = Get-Content .\New.txt
if ($old.Equals($new)) {
Write-Host "They are the same"
}
The files are the same, but it always evaluates to false. What am I doing wrong? Is there a better way to go about this?
Get-Content returns an array of strings. In PowerShell (and .NET) .Equals() on an array is doing a reference comparison i.e. is this the same exact array instance. An easy way to do what you want if the files aren't too large is to read the file contents as a string e.g.:
$old = Get-Content .\Old.txt -raw
$new = Get-Content .\Newt.txt -raw
if ($old -ceq $new) {
Write-Host "They are the same"
}
Note the use of -ceq here to do a case-sensitive comparison between strings. -eq does a case-insensitive compare. If the files are large then use the new Get-FileHash command e.g.:
$old = Get-FileHash .\Old.txt
$new = Get-FileHash .\New.txt
if ($old.hash -eq $new.hash) {
Write-Host "They are the same"
}
Related
I want this power shell script to search for the occurrence of multiple strings, one after the other, and to append the results in a .txt file.
Currently I am specifying the string that I want to look for, waiting for the script to finish looking for that string and transferring the results into a spreadsheet. This is taking a lot of time as I have to keep specifying the string I want to look for, especially since there are well over 100 that I need to look for.
#ERROR REPORTING ALL
Set-StrictMode -Version latest
$path = "C:\Users\username\Documents\FileName"
$files = Get-Childitem $path -Include *.docx,*.doc,*.ppt, *.xls,
*.xlsx, *.pptx, *.eap -Recurse | Where-Object { !($_.psiscontainer) }
$output =
"C:\Users\username\Documents\FileName\wordfiletry.txt"
$application = New-Object -comobject word.application
$application.visible = $False
$findtext = "First_String"
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files)
{
$document = $application.documents.open($file.FullName,$false,$true)
$range = $document.content
$wordFound = $range.find.execute($findText)
if($wordFound)
{
"$file.fullname has found the string called $findText and it is
$wordfound" | Out-File $output -Append
}
}
$document.close()
$application.quit()
}
getStringMatch
This script will look for 'First_String' successfully, I was hoping to be able to specify 'Second_String', 'Third_String' etc rather than replace First_String every time.
As an alternative to the suggestion from #Mathias, you could use Regex to query the document text instead.
Read the context of the document as a string $text = $document.content.text and then use Select-String $findtext -AllMatches to evaluate the matches with $findtext as string representation of a regular expression instead.
Example:
# pipe delimited string as a regular expression
$findtext = "First_String|Second_String|Third_String"
Function getStringMatch
{
# Loop through all *.doc files in the $path directory
Foreach ($file In $files)
{
$document = $application.documents.open($file.FullName,$false,$true)
$text = $document.content.text
$result = $text | Select-String $findtext -AllMatches
if($result)
{
"$file.fullname has found the strings called $($result.Matches.Value) at indexes $($result.Matches.Index)" | Out-File $output -Append
}
}
$document.close()
$application.quit()
}
Note that if you're trying find strings that do have reserved regex character, you'll need to escape them first
I'm fairly new to powershell.
I'm trying to compare data in a CSV File against random files in a specific folder.
I want to see if and what has changed and then log that in another column called "Changed".
Here's what I've done below, it seems to create a new column called 'Changed' but doesn't input the changes in it.
$Spreadsheet = 'C:\Powershell\CSV\inv.csv'
$SpreadSheetPath = "C:\Powershell\CSV"
Import-Csv $Spreadsheet -Delimiter "|" -Encoding Default | ForEach-Object -
{
$Path += $_.Path
$Filename += $_.Filename
$DateModified += $_.DateModified
$FileSize += $_.FileSize
$MD5Hash += $_.MD5Hash
}
{
$Msg1 = "Path changed"
$Msg2 = "File Name changed"
$Msg3 = "Date Modified changed"
$Msg4 = "File Size changed"
$Msg5 = "MD5 changed"
$Msg6 = "Files are the same"
$psdata = "D:\ps-test\data\*.*"
}
If (($Path -eq $psdata))
{
Import-Csv C:\Powershell\CSV\inv.csv |
Select-Object *,#{Name='Changed';Expression={$Msg6}} |
Export-Csv C:\Powershell\CSV\NewSpreadsheet4.csv
}
Else
{
Import-Csv C:\Powershell\CSV\inv.csv |
Select-Object *,#{Name='Changed';Expression={$Msg1}} |
Export-Csv C:\Powershell\CSV\NewSpreadsheet4.csv
}
Here is an example of what the CSV looks like:
Path Filename Date Modified File Size MD5 Hash
D:\ps-test\data adminmodeinfo.htm 03/11/2010 22:42 1079 BD1C9468D71FD33BB35716630C4EC6AC
E:\ps-test\data admintoolinfo.htm 03/11/2010 22:42 868 24B99B6316F0C49C23F27FEA6FF1C6AC
E:\ps-test\data admin_ban.bmp 03/11/2010 22:42 63480 C856F1F3C58962B456E749F2EA9C933A
E:\ps-test\data baseline.dat 03/20/2010 03:18:33 173818 F13183D88AABD1A725437802F8551A06
E:\ps-test\data blueRule.gif 03/11/2010 22:42 815 D1AEFE884935095DAB42DAFD072AA46F
E:\ps-test\data deffactory.dat 03/20/2010 03:18:33 706 862D4DFD2F49021BB7C145BDAFE62F6F
E:\ps-test\data dividerArt.jpg 03/11/2010 22:42 367 F7050C596C097C0B01A443058CD15E35
There are many issues with your code.I will try to highlight a few of the issues, link to documentation and point you in the right direction so that you can resolve your issues. A proper solution would require getting many more requirements, or writing code (off-topic for StackOverflow)
Change
| ForEach-Object -
{
to
| ForEach-Object {
In the Foreach-Object, you are concatenating values from each line because you are using +=.
On the first run, $Path contains D:\ps-test\data.
After the second run, it contains D:\ps-test\dataE:\ps-test\data.
At the end of your test data, it contains D:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\data
The messages are contained in a script block, but it does not look like this is intentional as this is never executed. So after the scriptblock, the variable $Msg1 has not been created; it's blank.
If (($Path -eq $psdata))
double brackets not required.
will always be false because the variable $psdata does not exist as it was stated inside a script block.
will always be false because you are attempting to equate the strings; your input does not literally contain "D:\ps-test\data\*.*". You probably want -like instead of -eq.
will always be inaccurate because even if the paths are compared, there is no check that the file actually exists on the system.
Useful links
Test-Path to check if file exists.
Get-FileHash to get MD5 hash and compare to file.
Get-ChildItem to get a list of directories/files in a directory.
Write-Output so that you can print variables and make sure they contain what you expect.
about_comparison_operators - -in and -contains will help you.
This is a suggestion to help you get started. It's not complete and not tested! Let me know if it works as expected and if you have any questions.
Import-Csv 'C:\Powershell\CSV\inv.csv' -Delimiter "|" -Encoding Default | foreach {
$Path += $_.Path
$Filename += $_.Filename
$DateModified += $_.DateModified
$FileSize += $_.FileSize
$MD5Hash += $_.MD5Hash
$file = [System.IO.FileInfo](Join-Path $Path $Filename)
if (-not $file.Exists) {
$message = "File does not exist"
}
elseif ($file.LastWriteTime -ne [DateTime]$DateModified) {
$message = "Dates differ"
}
elseif ($file.Length -ne [int]$FileSize) {
$message = "Sizes differ"
}
# and so on...
# (You cannot really compared a changed file name btw)
New-Object -Type PSObject -Prop #{
Path = $Path
Filename = $Filename
DateModified = $DateModified
FileSize = $FileSize
MD5Hash = $MD5Hash
Message = $message
}
} | Export-CSV 'C:\Powershell\CSV\NewSpreadsheet4.csv'
I want to compare 2 strings in PowerShell. One is the actual date and the other will be read from a file that contains a lot of rows. The row always will contain the same structure, then we can extract a substring for compare the date.
The file will be a plain text. The types of a date and the substring are the same.
MyProblem:
If I execute this code the program doesn't write anything, the Write-Host is not executing, even if the strings are the same. Can someone help me?
$list = Import-Csv C:\file.txt
#actual date
$date = Get-date -Format d
$day = $fecha.Substring(0,2)
$month = $fecha.Substring(3,2)
$year = $fecha.Substring(6,4)
$date = "$year$month$day"
#I do this because if I use $list will return me an pscustomobject object
$file = Get-Content -Path C:\file.txt
#Use a ForEach loop to process all lines in the source file
foreach ($row in $file) {
$sub = $entrada.Substring(7,7)
if ($date-like $sub) {Write-Host "They are equals"}
if ($sub -Match $date) {Write-Host "They are equals"}
if ($date.Equals($sub)) {Write-Host "They are equals"}
if ($date-eq $sub) {Write-Host "They are equals"}
if ($sub -contains $date) {Write-Host "They are equals"}
}
Your reference date string is 8 characters long, but the string you extract from the lines has only 7 characters, so it's unlikely you'll ever get a match. Particularly since you carefully chose your comparisons to avoid even accidental matches. ;) Also, as #arco444 pointed out in the comments to your question, your loop variable $row is never used anywhere inside the loop.
I would suggest to simplify the code to something like this:
$date = Get-Date -f 'yyyyMMdd'
Get-Content -Path 'C:\file.txt' | Where-Object { $_.Substring(7,8) -eq $date }
That would list only those lines from the input file that contain a matching date.
Another option would be to use the Contains() method on each line:
Get-Content -Path 'C:\file.txt' | Where-Object { $_.Contains($date) }
but that would find a matching date anywhere in a line, not just at the given position.
I'd avoid using wildcard (-like) or regular expression (-match) checks, since you want to compare a fixed value, not a pattern. And you can't use the -contains operator, because that one is for checking if an array contains a particular element.
It looks like "entrada" and "row" were intended to refer to the same data item.
Try it like this:
$list = Import-Csv C:\file.txt
#actual date
$date = Get-date -Format d
$day = $fecha.Substring(0,2)
$month = $fecha.Substring(3,2)
$year = $fecha.Substring(6,4)
$date = "$year$month$day"
#I do this because if I use $list will return me an pscustomobject object
$file = Get-Content -Path C:\file.txt
#Use a ForEach loop to process all lines in the source file
foreach ($entrada in $file) {
$sub = $entrada.Substring(7,7)
if ($date-like $sub) {Write-Host "They are equals"}
if ($sub -Match $date) {Write-Host "They are equals"}
if ($date.Equals($sub)) {Write-Host "They are equals"}
if ($date-eq $sub) {Write-Host "They are equals"}
if ($sub -contains $date) {Write-Host "They are equals"}
}
I have a list of files in a folder each are in this format: custID_invID_prodID or custID_invID_prodID_Boolvalue. For every file I need to break it into sections based on '_'. Currently I have this code:
$files = Get-ChildItem test *.txt
foreach($f in $files){
$file = #()
$file += ([String]$f).Split("_")
$total = ([String]$f).Split("_") | Measure-Object | select count
Write-Host "${total}"
if($total -eq 2) {
for($i = 2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
Write-Host "${file}"
}
}
}
The problem is that Write-Host "${total}" equals #{Count=#} where # is real number of times "_" is found in file. How can I use $total inside my if statement to do different operations based upon the number of "_" found?
Would it not be simpler just to assign the parts you want directly to named variables rather than working with an array?
foreach($f in (Get-ChildItem test *.txt)) {
$custId, $invID, $prodID, $Boolvalue = $f.BaseName -split "_"
Write-Host $custId, $invID, $prodID, $Boolvalue
}
If the name only has 3 parts this will simply set $Boolvalue to an empty string.
Also note that you don't have to trim the extension off the last element after splitting, just use the BaseName property to get the name without extension.
You need to get the count-property value, like $total.count in your if test. You could also clean it up like this.
$files = Get-ChildItem test *.txt
foreach($f in $files){
$file = #(([String]$f).Split("_"))
Write-Host "$($file.Count)"
if($file.Count -eq 2) {
for($i = 2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
Write-Host "${file}"
}
}
}
If you had included more information about what you were trying to do, we could clean it up alot more. Ex. It's seems like you want to do something like this:
Get-ChildItem test *.txt | ForEach-Object {
$file = #($_.BaseName.Split("_"))
Write-Host "$($file.Count)"
if($file.Count -eq 2) {
Write-Host $file
}
}
Seems to me that you're doing it the hard way. Why not:
$x = "aaa_bbb_ccc"
$cnt = $x.Split("_").count
I need to create a script to search through just below a million files of text, code, etc. to find matches and then output all hits on a particular string pattern to a CSV file.
So far I made this;
$location = 'C:\Work*'
$arr = "foo", "bar" #Where "foo" and "bar" are string patterns I want to search for (separately)
for($i=0;$i -lt $arr.length; $i++) {
Get-ChildItem $location -recurse | select-string -pattern $($arr[$i]) | select-object Path | Export-Csv "C:\Work\Results\$($arr[$i]).txt"
}
This returns to me a CSV file named "foo.txt" with a list of all files with the word "foo" in it, and a file named "bar.txt" with a list of all files containing the word "bar".
Is there any way anyone can think of to optimize this script to make it work faster? Or ideas on how to make an entirely different, but equivalent script that just works faster?
All input appreciated!
If your files are not huge and can be read into memory then this version should work quite faster (and my quick and dirty local test seems to prove that):
$location = 'C:\ROM'
$arr = "Roman", "Kuzmin"
# remove output files
foreach($test in $arr) {
Remove-Item ".\$test.txt" -ErrorAction 0 -Confirm
}
Get-ChildItem $location -Recurse | .{process{ if (!$_.PSIsContainer) {
# read all text once
$content = [System.IO.File]::ReadAllText($_.FullName)
# test patterns and output paths once
foreach($test in $arr) {
if ($content -match $test) {
$_.FullName >> ".\$test.txt"
}
}
}}}
Notes: 1) mind changed paths and patterns in the example; 2) output files are not CSV but plain text; there is not much reason in CSV if you are interested just in paths - plain text files one path per line will do.
Let's suppose that 1) the files are not too big and you can load it into memory, 2) you really just want the Path of the file, that matches (not the line etc.).
I tried to read the file only once and then iterate through the regexes. There is some gain (it's a faster then the original solution), but the final result will depend on other factors like file sizes, count of files etc.
Also removing 'ignorecase' makes it faster a little bit.
$res = #{}
$arr | % { $res[$_] = #() }
Get-ChildItem $location -recurse |
? { !$_.PsIsContainer } |
% { $file = $_
$text = [Io.File]::ReadAllText($file.FullName)
$arr |
% { $regex = $_
if ([Regex]::IsMatch($text, $regex, 'ignorecase')) {
$res[$regex] = $file.FullName
}
}
}
$res.GetEnumerator() | % {
$_.Value | Export-Csv "d:\temp\so-res$($_.Key).txt"
}