Answered by:
Power Shell: For Each Loop, Array, and XML output troubles

Question
-
I had previously asked a question about this script, but the portion I needed help with got fixed, so I decided to ask a new question.
I am working on a PS script that takes the hash of each file in a directory and a few specific files located elsewhere, combines them into a csv, and then compares them with a previously captured baseline. Then there is a for each loop in which each file with a differing hash is output to an XML file with info including algorithm, hash, filepath, and last modified date.
### This PowerShell 4.0 script compares the MD5 hash of each file in ### ### a directory in it's current state and compares it with a baseline ### ### copy taken previously. If no baseline exists, a new one is created. ### ### ***The first time this script is run, a false-positive report ### # Variables $DateTime = Get-Date -format M.d.yyyy.hh.mm.ss $Hashstore = "d:\Baseline.csv" $HashCompare = "d:\Difference_$DateTime.csv" $HashTemp = "d:\hashtemp_$DateTime.csv" $FileDir = "d:\scripts\" $ScriptLocation = "d:\FileCheckerv2.ps1" $CRQLocation = "d:\Change Request.csv" $OutPut = "d:\Results_$DateTime.xml" # Check if Baseline.txt Exists If (Test-Path $Hashstore) # // File exists { } Else { # // File does not exist - Should never happen! $RefreshHash = dir $FileDir -Recurse | Get-FileHash -Algorithm MD5 $RefreshScript = Get-FileHash $ScriptLocation -Algorithm MD5 $RefreshCRQ = Get-FileHash $CRQLocation -Algorithm MD5 $RefreshHash | Export-Csv -Path $Hashstore -NoTypeInformation -Force $RefreshScript | Export-Csv -append -Path $Hashstore -NoTypeInformation -Force $RefreshCRQ | Export-Csv -append -Path $Hashstore -NoTypeInformation -Force } ## Generate new Compare Hash.txt $HashNew = dir $FileDir -Recurse | Get-FileHash -Algorithm MD5 $HashNew | Export-Csv -Path $HashCompare -NoTypeInformation -Force #Get hash of this script $HashScript = Get-FileHash $ScriptLocation -Algorithm MD5 $HashScript | Export-Csv -append -Path $HashCompare -NoTypeInformation -Force #Get hash of CRQ Log $HashCRQ = Get-FileHash $CRQLocation -Algorithm MD5 $HashCRQ | Export-Csv -append -Path $HashCompare -NoTypeInformation -Force # Get Hash of baseline.txt $HashBaseline = Get-FileHash -Path $Hashstore -Algorithm MD5 # Get Hash of hashcompare.txt $HashDiff = Get-FileHash -Path $HashCompare -Algorithm MD5 # Create Array for differences $DiffObjCollection = @() # If changed, output hash to storage, and flag changes If ($HashBaseline.hash -eq $HashDiff.hash) { Add-Content -Path D:\success.$DateTime.txt -Value " Source Files ARE EQUAL </p>" } Else { Add-Content -Path D:\failure.$DateTime.txt -Value "Source Files NOT EQUAL </p>" # Storing a collection of differences in $Diffs $Diffs = Compare-Object -ReferenceObject (Import-Csv $Hashstore) -DifferenceObject (Import-Csv $HashCompare) Foreach ($Diff in $Diffs) { $DiffHashInfo = $Diff | Select-Object -ExpandProperty InputObject $DiffFileInfo = Get-ChildItem -Path $Diff.Path # Creating a list of properties for the information you need $DiffObjProperties = [ordered]@{'Algorithm'=$DiffHashInfo.Algorithm 'Hash'=$DiffHashInfo.Hash 'File Path'=$DiffFileInfo.FullName 'Last Edit Time'=$DiffFileInfo.LastWriteTime } # Building a custom object from the list of properties in $DiffObjProperties $DiffObj = New-Object -TypeName psobject -Property $DiffObjProperties $DiffObjCollection += $DiffObj } } $DiffObjCollection | ConvertTo-Xml -As String -NoTypeInformation | Write-host
My understanding is that the loop would take each file that was found different, cast the fields I need, and then append them all to a file. The last line converts these to xml and outputs them.
My issue is the output. The baseline and difference files are created correctly, and the success.txt/fail.txt files are generated and accurate. The problem is that the xml either comes up blank, or only lists files that didnt previously exist. After getting some adviceI have added the $DiffObjCollection array, but it is not piping out any results.
- Moved by Bill_Stewart Thursday, September 25, 2014 8:41 PM This is not "fix script for me" forum
Monday, August 25, 2014 5:48 PM
Answers
-
This is closer to what you want anyway. The original code could never have worked.
$DateTime = Get-Date -format M.d.yyyy.hh.mm.ss $Hashstore='d:\Baseline.xml' $HashCompare="d:\Difference_$DateTime.xml" $files=@('d:\scripts\','d:\FileCheckerv2.ps1','d:\Change Request.csv') #' $output='d:\Results_$DateTime.xml' $current=Get-FileHash $files -Algorithm MD5 if($baseline=Import-Clixml $hashstore){ # compare Write-Host 'Comparing file hashes' -BackgroundColor green $results=Compare-Object $baseline $current $results | Export-CliXml $output }else{ Write-Host 'Original not found' -ForegroundColor red $current | Export-Clixml $Hashstore }
¯\_(ツ)_/¯
- Proposed as answer by Sam Boutros Wednesday, August 27, 2014 11:45 PM
- Marked as answer by Just Karl Tuesday, June 2, 2015 10:59 PM
- Edited by jrv Tuesday, June 2, 2015 11:08 PM
Monday, August 25, 2014 7:04 PM
All replies
-
Sorry but this is not a custom fix it forum. You need to ask the people who wrote this script to fix it for you.
If you cannot find the authors then you will do best by contacting a consultant with scripting experience.
I am sorry but this forum is for technicians who use scripting or for those learning to write scripts. It is not a free script writing forum.
¯\_(ツ)_/¯
Monday, August 25, 2014 6:50 PM -
I wrote this script myself with help from others. I'm new to PS so I keep running into simple things that are roadblocks.
I am indeed actively learning to write scripts, this being one of them.
Im sorry but I don't see the difference between "technicians who use scripting or for those learning to write scripts" and myself, as this is exactly what I am trying to do.
I mean no sarcasm or respite, I am just looking for help from someone more knowledgeable than myself.
If this isn't the place, I'll go elsewhere.
Thanks
Monday, August 25, 2014 6:59 PM -
This is closer to what you want anyway. The original code could never have worked.
$DateTime = Get-Date -format M.d.yyyy.hh.mm.ss $Hashstore='d:\Baseline.xml' $HashCompare="d:\Difference_$DateTime.xml" $files=@('d:\scripts\','d:\FileCheckerv2.ps1','d:\Change Request.csv') #' $output='d:\Results_$DateTime.xml' $current=Get-FileHash $files -Algorithm MD5 if($baseline=Import-Clixml $hashstore){ # compare Write-Host 'Comparing file hashes' -BackgroundColor green $results=Compare-Object $baseline $current $results | Export-CliXml $output }else{ Write-Host 'Original not found' -ForegroundColor red $current | Export-Clixml $Hashstore }
¯\_(ツ)_/¯
- Proposed as answer by Sam Boutros Wednesday, August 27, 2014 11:45 PM
- Marked as answer by Just Karl Tuesday, June 2, 2015 10:59 PM
- Edited by jrv Tuesday, June 2, 2015 11:08 PM
Monday, August 25, 2014 7:04 PM -
I wrote this script myself with help from others. I'm new to PS so I keep running into simple things that are roadblocks.
I am indeed actively learning to write scripts, this being one of them.
Im sorry but I don't see the difference between "technicians who use scripting or for those learning to write scripts" and myself, as this is exactly what I am trying to do.
I mean no sarcasm or respite, I am just looking for help from someone more knowledgeable than myself.
If this isn't the place, I'll go elsewhere.
Thanks
I posted an easier approach that uses PowerShell tools. You have a major syntax error and have not shown it. The script, as posted, cannot be executed. Start by fixing your errors.
Look at this:
If ($HashBaseline.hash -eq $HashDiff.hash) {
Add-Content -Path D:\success.$DateTime.txt -Value " Source Files ARE EQUAL </p>"
}
Else {
Add-Content -Path D:\failure.$DateTime.txt -Value "Source Files NOT EQUAL </p>"
It is syntactically incorrect. Too many {{{{
¯\_(ツ)_/¯
Monday, August 25, 2014 7:11 PM -
I double checked my open/close of {}. I have a foreach embedded in the else condition, but both ISE and my eyeballs can't find an extra {. Also, i can execute in PS console just fine, I just don't get the output I need. What I want is simply the name, path, last modified date of each file that has changed from the directory or the two other files.
What you provided is definitely a more concise version! I attempted to run it, but just return an xml file listing the properties of the two files, and no data on the directory I want to point to.
Just curious, why would my original never work, sans any minor syntax errors? From my understanding my script takes the hash of every file in the directory, the hash of the two other files, dumps them into a single csv, and compares it to a previously generated baseline on a line by line basis. The only part I figured to be broken was the array and insertion of each changed file into said array. From there it won't output into xml, but I need to get it to at least pipe the changes into the array....
Thanks for your patience!
Monday, August 25, 2014 7:31 PM -
Interesting. On the IE screen it looks OK but when I copy and paste it there is one extra {
The color is also very badly scrambled.
Anyway. Start with what I posted as yours is too convoluted for me to follow. If you can find a simple way to show what is wrong I will try to help. THe script is over written in my view. Just grab all the hashes and compare them as I have done. You do not need loops in loops.
¯\_(ツ)_/¯
Monday, August 25, 2014 7:35 PM -
OK so I'm working with a hammer, not a paintbrush.
I tried to simplify as much as possible, clustering the target files together as you did. I still don't know how to get a line item result of each file that has changed with the name, filepath, last modified time without using a ForEach embedded in the Else statement.
I still haven't gotten it to work, for some reason it is giving me the info from the root of my profile, i.e. C:\user\folder. The masterpiece that I posted originally would output the fields exactly as I wanted, but it would only give me one result, even though I was looping through each file and outputting it to an array.
Ignoring my crappy attempt at the ForEach statement, what would be the easiest way to get the line item results in an xml file?
This is where I'm stuck.
$DateTime = Get-Date -format M.d.yyyy.hh.mm.ss $Baseline ='d:\Baseline.txt' $DiffFile = "d:\DiffFile.$DateTime.txt" $output = "d:\results.$DateTime.xml" $files=@('d:\scripts\*','d:\FileCheckerv2.ps1','d:\Change Request.csv') # VERIFY BASELINE EXISTS -- IF NOT CREATE NEW if (Test-Path $Baseline) { } else { $RefreshHash = Get-FileHash $files -Algorithm MD5 | Export-Csv $Baseline } $Compare = Get-FileHash $files -Algorithm MD5 | Export-Csv $DiffFile $BaseHash = Get-FileHash $Baseline $CompareHash = Get-FileHash $DiffFile # Create Array for differences $DiffObjCollection = @() if($BaseHash -eq $CompareHash) { } else { $Diffs = Compare-Object -ReferenceObject (Import-Csv $baseline) -DifferenceObject (Import-Csv $DiffFile) Foreach ($Diff in $Diffs) { $DiffHashInfo = $Diff | Select-Object -ExpandProperty InputObject $DiffFileInfo = Get-ChildItem -Path $DiffHashInfo.Path # Creating a list of properties for the information you need $DiffObjProperties = [ordered]@{'Algorithm'=$DiffHashInfo.Algorithm 'Hash'=$DiffHashInfo.Hash 'File Path'=$DiffFileInfo.FullName 'Last Edit Time'=$DiffFileInfo.LastWriteTime } # Building a custom object from the list of properties in $DiffObjProperties $DiffObj = New-Object -TypeName psobject -Property $DiffObjProperties $DiffObjCollection += $DiffObj } } $DiffObjCollection | ConvertTo-Xml -As String -NoTypeInformation | Out-File $output
Again, thanks!
- Edited by SudoNotRecommended Monday, August 25, 2014 8:39 PM
Monday, August 25, 2014 8:26 PM -
Compare-Object does that in one line. All changed values are reported. No change then no output.
You are making this into a huge issue because you do not understand data and you do not understand PowerShell or scripting.
The code I posted does everything your script is trying to do. It needs no loops.
PowerShell almost never needs loops. That is what is exceptional about it. Learn it and you will see how it simplifies. PowerShell auto-enumerates. Loop constructs are call enumerators.
You are building and rebuilding things that PowerShell has already built. Look at the output of Get-FileHash. It has everything you need as an object.
Compare to files by hash.
PS C:\scripts> $f1=get-filehash test.txt PS C:\scripts> $f2=get-filehash \temp\test.txt PS C:\scripts> Compare-Object $f1 $f2 PS C:\scripts> Compare-Object $f1 $f2 -Property hash PS C:\scripts> Compare-Object $f1 $f2 -Property hash -ExcludeDifferent PS C:\scripts> Compare-Object $f1 $f2 -Property hash -IncludeEqual hash SideIndicator ---- ------------- D20DDCBAFD1D3B5E03606113F5FF5D3065C10C0EE0E6A9799979FE4952C0E748 ==
You can feed paths and files to Get-FielHash
Get-FileHash c:\temp\*,c:\temp3\*
You can store all of this in XML and return it and compare later. Export-CliXML and Import-CliXml.
Once you learn some more about WIndows, files and scirtping you wil lfind many this take only a very few lines.
¯\_(ツ)_/¯
Monday, August 25, 2014 9:23 PM -
Ok Ok simmer down. I am learning this stuff, I would think that would indicate that I don't know the ins and outs of PS commands.
Anyhoo.
Here is what I have come up with:
$DateTime = Get-Date -format M.d.yyyy.hh.mm.ss $Hashstore='d:\Baseline.xml' $HashCompare='d:\Difference.xml' $output="d:\Results_$DateTime.xml" $files=@('d:\scripts\*','d:\FileCheckerv2.ps1','d:\Change Request.csv') if(test-path $hashstore){ } else { Get-FileHash $files -Algorithm MD5 | Export-Clixml $Hashstore } $current=Get-FileHash $files -Algorithm MD5 | Export-Clixml $HashCompare if($HashStore = $HashCompare) { Write-Host 'Files are the same' -BackgroundColor green }else{ Write-Host 'Differences Found!' -ForegroundColor red # compare $results = Compare-Object $baseline $HashCompare $results | Export-CliXml $output Get-FileHash $files -Algorithm MD5 | Export-Clixml $Hashstore }
This is reaaally close, but for some reason, if I go and change the contents of one of the files, the difference object will reflect the new hash, but will still give me the true of the if statement. Also, when I run
Compare-Object $baseline $HashCompare
It seems like it tries to compare the contents of the file that is $baseline with the file that is $hashcompare, a la
PS D:\> Compare-Object $baseline $HashCompare | Write-Host @{InputObject=d:\Difference.xml; SideIndicator==>} @{InputObject=; SideIndicator=<=} @{InputObject=; SideIndicator=<=} @{InputObject=; SideIndicator=<=} @{InputObject=; SideIndicator=<=}
Again, thanks for being patient with me.
Tuesday, August 26, 2014 1:39 AM -
Why do you guys shit your pants any time someone says the word "learn". It reminds me of Maynard G. Krebs on the Dobie Gillis Show. He would freak anytime anyone said the word "work". He would scream "WORK" and run out the door.
Without learning you are just a monkey copying what you see.
You are trying to use commands thatyou do not understand then wondering why they do not give you an answer you understand. You fail to recognize PowerShell standard output. All of this is because you do not have a clue as to how a script works or to how PowerShell works.
Please take the time to learn at least some of the basics of PowerShell.
Consider how hard it is for us to show you anything when you do not understand what is being share.
Start by learning how to use help then run full help on all CmdLets. Look at the examples and do the examples until youunderstand how the CmdLet works. Once you learnthese simple things you will be able ot use PowerShell and understand much of what is happening.
¯\_(ツ)_/¯
Tuesday, August 26, 2014 1:58 AM -
Please take the time to learn at least some of the basics of PowerShell.
Consider how hard it is for us to show you anything when you do not understand what is being share.
I appreciate this. This is helpful advice, and you are right, I do not yet understand what goes on behind the scenes with PS, I am very new and at this time am more focused on the result than the means.
The first time you used a fork, you did not know how it worked. You learned by doing. This is what I'm doing. Spending hours off work trying to learn how to do something new. I asked for help because I have been stuck for a while, and the whole "learning from others" has worked pretty damn well for the last millennia +. If you would kindly read my posts at the beginning, it is full of words like understanding, advice, explain. Never once did I say "do this for me". If I do that, I will be right back here again next week.
I feel you are channeling a little rage towards more than just me for the above reason. But instead of saying what needs to be said, you are inflammatory. Telling someone they "know nothing about XYZ" is a tad offensive when you are talking to strangers.
I have been doing examples with full help for a while, but being new, as I might have mentioned once or twice before, the dots don't always connect until you see the line for yourself.
I appreciate the constructive parts of your posts, it has given me some guidance on how to finish my project, and on PS script construction. The rest of your complaining I could do without. If you really can't stand helping, you could just simply NOT REPLY.
¯\_(ツ)_/¯
Tuesday, August 26, 2014 2:31 AM -
As I tried to point out in my version. You can compare the file hashes very easily but you must understand what is happening and how things are compared.
If you get to hash lists.
$list1=Get-FileHash $files
$list2=Get-FileHash $files2Then compare
Compare-Object $list1 $list2 -Property hash
This will list all files that are not the same.
Read the help and documentation. Try to understand how this works. Do not try and change it until you really understand.
¯\_(ツ)_/¯
Tuesday, August 26, 2014 2:32 AM -
I appreciate you still trying to help. I'm going to spend the rest of the evening looking over the concept you just shared, and others. If I do figure out what I'm trying to do I;ll post it back :)Tuesday, August 26, 2014 2:34 AM
-
Please take the time to learn at least some of the basics of PowerShell.
Consider how hard it is for us to show you anything when you do not understand what is being share.
I appreciate this. This is helpful advice, and you are right, I do not yet understand what goes on behind the scenes with PS, I am very new and at this time am more focused on the result than the means.
The first time you used a fork, you did not know how it worked. You learned by doing. This is what I'm doing. Spending hours off work trying to learn how to do something new. I asked for help because I have been stuck for a while, and the whole "learning from others" has worked pretty damn well for the last millennia +. If you would kindly read my posts at the beginning, it is full of words like understanding, advice, explain. Never once did I say "do this for me". If I do that, I will be right back here again next week.
I feel you are channeling a little rage towards more than just me for the above reason. But instead of saying what needs to be said, you are inflammatory. Telling someone they "know nothing about XYZ" is a tad offensive when you are talking to strangers.
I have been doing examples with full help for a while, but being new, as I might have mentioned once or twice before, the dots don't always connect until you see the line for yourself.
I appreciate the constructive parts of your posts, it has given me some guidance on how to finish my project, and on PS script construction. The rest of your complaining I could do without. If you really can't stand helping, you could just simply NOT REPLY.
¯\_(ツ)_/¯
I am not a "channeller" I quit Krishnamurti decasdes ago. I have no rage. I am just trying to wake up a very lazy student.
You can learn this but you must put in an effort. Just typing lines innotepad with no understanding will not lead myou to enlightenment - only to disillusion.
¯\_(ツ)_/¯
Tuesday, August 26, 2014 2:35 AM -
I appreciate you still trying to help. I'm going to spend the rest of the evening looking over the concept you just shared, and others. If I do figure out what I'm trying to do I;ll post it back :)
Start at the beginning. Do all of the exercises. You will understand after t requisite effort has been given.
http://technet.microsoft.com/en-us/scriptcenter/dd793612.aspx
¯\_(ツ)_/¯
Tuesday, August 26, 2014 2:37 AM