I am looking to do a hash compare of a variety of things; files, folders, registry properties and registry keys.
File hash is easy...
$Hash1 = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($filePath1)))
$Hash2 = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($filePath2)))
if ($Hash1 -eq $Hash2) {}
And folder is easy because I can do a get Get-ChildItem, and if the resulting list isn't identical then the compare is false, and if the list is identical then if any individual file hash isn't identical then the compare is false.
However, I am trying to add registry stuff to this, and I am not clear on how to do the ReadAllBytes() on a registry property. Or, am I really over thinking this since a property is not a complex file, and I should just first check for property kind with $sourceKey.GetValueKind($name) and then compare actual values if the kinds match? Does -eq work with every possible data type in the registry? Or are there gotchas I need to worry about?
Related
long time listener first time caller.
Normally I am pretty good at finding and digging and getting what I need and then modifying it to suit. This one seems to be a little trickier than what I have managed to pull off before. I am self taught in PowerShell mostly out of curiosity to see what I can do.
I am trying to create a report from data from 2 CSVs, and "most" of the data in the 2 CSVs are identical. There is simply 1 column of data in one of the CSVs that I want to add to the other one. I live regularly in the world of excel and I can do this with a formula in a matter of seconds [=VLOOKUP(H8,C:C,2,FALSE)] but accomplishing the same goal in PowerShell seems to be eluding me.
As I mentioned, I tend to try and find others who have done similar things and modify it. The best sounding one I found here ( Combine data from 2 CSV files into 1 new CSV using Powershell ) and I am still trying to play with the code on that site. Sometimes I find something and I try and stick with it too long where there might be another command that I am not familiar with that is better suited to what I should be looking at and might just need a pointer in that direction.
But here is a visual representation of what I am trying to do.
And every email address in File 2, is present in File 1.
Use Import-Csv to parse both CSV input files into arrays of [pscustomobject] instances for OOP processing.
For file 2, build a hashtable that maps the Email column values to their License values.
Then use a calculated property with Select-Object to append a property to the objects parsed from file 1, using the hashtable to map each Email property to the License value from file 2; if there is no hashtable entry for a given Email property value, $null is returned, which in the context of exporting to CSV (with Export-Csv) amounts to an empty field (column value).
# Import file 2 and create a hashtable that maps each Email
# column value to the License column value.
$ht = #{}
Import-Csv File2 | ForEach-Object { $ht[$_.Email] = $_.License }
# Import file 1 and append a License column that contains
# the license value from file 2 if the Email column value matches.
Import-Csv File1 |
Select-Object *, #{ Name='License'; Expression={ $ht[$_.Email] } }
# | Export-Csv ... # complete as needed
These things drive me nuts:
Is there an easy way to have Powershell just show me the empty string and the list with an empty string in it?
For a while I am maintaining a ConvertTo-Expression which converts an (complex) object into a PowerShell expression which eventually can be used to rebuild most objects. It might useful in situations as comparing test results but also to reveal objects. For details see: readme.
Source and installation
The ConvertTo-Expression script can be installed from the PowerShell Gallery:
Install-Script -Name ConvertTo-Expression
As it concerns a standalone script, installation isn't really required. If you don't have administrator rights, you might just download the script (or copy it) to the required location. You might than simply invoke the script using PowerShell dot sourcing:
. .\ConvertTo-Expression.ps1
Example
The following command outputs the same expression as used to build the object:
$Object = [Ordered]#{
'Null' = $Null
'EmptyString' = ''
'EmptyArray' = #()
'SingleItem' = ,''
'EmptyHashtable' = #{}
}
ConvertTo-Expression $Object
Note the comment from #Mathias there's no functional difference between "one string" and "an array of one string", the pipeline consumes 1 string either way. PowerShell is not node which is described here: PowerShell enumerate an array that contains only one inner array. Some objects might be really different than you expect.
See also: Save hash table in PowerShell object notation (PSON)
This is PowerShell, not Node. So it's not JavaScript or JSON. Also, PowerShell is not Bash or CMD any other regular text-based shell. PowerShell works with objects. .NET objects, in particular. And how objects are represented as text is ... quite a matter of taste. How to represent null? Of course: nothing. How to represent an empty string? Nothing, either. An empty array ... you get my point.
All pipeline output is by default send to Out-Default. In general, the way objects are represented can be controlled by format files: about_Format.ps1xml and about_Types.ps1xml. From PowerShell 6 upwards, the default formats are compiled into the source code, but you can extend them. How you do so, depends on your personal taste. Some options were already mentioned ConvertTo-Json "", ConvertTo-Json #("")), but this would be veryyy JSON-specific.
tl;dr Don't care too much about how objects are represented textually. As you see, there are many possible ways to do so, and also some others. Just make sure your scripts are always object-oriented.
You mean like Python's repr() function? A serialization? "Give me a canonical representation of the object that, when property evaluated, will return an object of the type originally passed?" No, that's not possible unless you write it yourself or you serialize it to XML, JSON, or similar. That's what the *-CliXml and ConvertTo-*/ConvertFrom-* commands are for.
On Powershell 7:
PS C:\> ''.Split(',') | ConvertTo-Json -Compress -AsArray
[""]
PS C:\> '1,2,3,,5'.Split(',') | ConvertTo-Json -Compress -AsArray
["1","2","3","","5"]
The closest would be the ToString() common method. However, that's intended for formatting output and typecasting, not canonical representations or serializations. Not every object even has a string representation that can be converted back into that object. The language isn't designed with that in mind. It's based on C#, a compiled language that favors binary serializations. Javascript requires essentially everything to have a string serialization in order to fulfill it's original design. Python, too, has string representations as fundamental to it's design which is why the repr() function is foundational for serialization and introspection and so on.
C# and .Net go the other way. In a .Net application, if you want to specify a literal empty string you're encouraged to use String.Empty ([String]::Empty in Powershell) because it's easier to see that it's explicit. Why would you ever want a compiled application to tell the user how the canonical representation of an object in memory? You see why that's not a useful thing for C# even if it might be for Powershell?
I have two lists saved in variables $list1 and $list2.
Now I want to compare them. Somehow, Compare-Object is giving me the wrong output, even though the user is in both lists it still says
not equal
I am trying to use Powershell to check a particular registry key we use on servers in HKLM\SOFTWARE to identify their status as an Alpha, Beta or General server. I am not seeing an easy way to do this. I took a look at the Get-ItemProperty cmdlet, but that doesn't allow me to actually select the exact key within SOFTWARE that has the data I need to build out an If Else loop that can output the server type.
In its simplest form, I would like to use this Powershell script to read that key (to see if Alpha, Beta, General) and then depending on which, output simply Alpha, Beta or General as a text file.
Have you tried Get-Item?
$keyInfo = Get-Item -Path $Key
#Where Key is something like hklm:\Software..
#This will give you all the subkeys to that key
$keyInfo.GetSubKeyNames()
#This will give you data stored in the values of the key.
$keyInfo.GetValue($ValueName, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
I have two registry files (.reg) exported using powershell. I would like to compare the difference of the two files to the registry, ideally using powershell. I have been using compare-object but that compares the files at the text level. I want to "pre-load" the files into memory and compare them at the key/property level to determine which keys have changed. I would then want to create a third .reg file with the changes and apply this to the registry.
Is this possible, eg using the Compare-Object?
Multiple ideas, none of them what I would call good. I cannot find a better way, even using .NET APIs.
(Compare-Object): Use 'reg.exe' to export the target tree to a file, then Get-Content both files, and do a Compare-Object on their contents.
(Manual): Use get-content on new.reg, then parse each line with either split or regex-fu. For each item, get-itemproperty on the target and validate the values of the properties and child keys
(Compare-Object): Import new.reg into temporary registry location, then use Get-ChildItem on both trees and compare all the objects