MD5-Checksum hashing with powershell for a whole directory - powershell

I'm trying to generate an MD5-Checksum with powershell for a whole directory.
On Linux there is a 1-liner that works just great, like this one:
$ tar -cf - somedir | md5sum
I learned that "tar" is now part of Windows10 and that it can be adressed in the PowerShell.
So I tried this:
tar -cf - C:\data | Get-FileHash -Algorithm MD5
What I get from PowerShell is this:
tar.exe: Removing leading drive letter from member names
Get-FileHash : the input object cannot be bound to any parameters of the command because the command does not accept pipeline input or the input and its properties do not match any of the parameters that accept pipeline Input
My Shell is set to german, so I ran the german error text through a Translation machine (https://www.translator.eu/#).
I wondered why I get this particular error message, because Get-FileHash IS able to process pipelined Input, e.g.:
ls | Get-FileHash -Algorithm MD5
This command just works like a charm, but it gives me checksums for each and every file.
What I want is 1 checksum for a complete given directory.
So, I probably messed up something… - any ideas?

EDIT: Here's an alternate method that is consistent even if all the files are moved/copied to another location. This one uses the hashes of all files to create a "master hash". It takes longer to run for obvious reasons but will be more reliable.
$HashString = (Get-ChildItem C:\Temp -Recurse | Get-FileHash -Algorithm MD5).Hash | Out-String
Get-FileHash -InputStream ([IO.MemoryStream]::new([char[]]$HashString))
Original, faster but less robust, method:
$HashString = Get-ChildItem C:\script\test\TestFolders -Recurse | Out-String
Get-FileHash -InputStream ([IO.MemoryStream]::new([char[]]$HashString))
could be condensed into one line if wanted, although it starts getting harder to read:
Get-FileHash -InputStream ([IO.MemoryStream]::new([char[]]"$(Get-ChildItem C:\script\test\TestFolders -Recurse|Out-String)"))
Basically it creates a memory stream with the information from Get-ChildItem and passes that to Get-FileHash.
Not sure if this is a great way of doing it, but it's one way :-)

Related

how to filter hash in output of powershell

i am trying to make a powershell script to automatically get the hash of a file for me since i do not want to install any 3rd party applications for it
i plan to put it in the *\shell\ registry to that i can easily access it via right-click
currently i have this for sha1
[HKEY_CLASSES_ROOT\*\shell\hash\shell\01menu]
"MUIVerb"="SHA1"
[HKEY_CLASSES_ROOT\*\shell\hash\shell\01menu\command]
#="powershell -WindowStyle hidden -command get-filehash -literalpath '%1' -algorithm SHA1 | fl | clip"
my problem is that the ouput of that is
Algorithm : SHA1
Hash : AEA8544C715835248EB1A5FE782D75D6730BEA9F
Path : %PATH%\%filename%
i just want to be able to filter out the hash part so that if i run the code i will just get
AEA8544C715835248EB1A5FE782D75D6730BEA9F
without all the other stuff
how to do this?
thanks!
Or if not using .Net notation...
(A little longer and a little slower)
Get-FileHash -LiteralPath '%1' -Algorithm SHA1 | Select -ExpandProperty Hash

How to display the file a match was found in using get-content and select-string one liner

I am attempting to search a directory of perl scripts and compile a list of all the other perl scripts executed from those files(intentionally trying to do this through Powershell). A simplistic dependency mapper, more or less.
With the below line of code I get output of every line where a reference to a perl file is found, but what I really need is same output AND the file in which each match was found.
Get-Content -Path "*.pl" | Select-String -Pattern '\w+\.pl' | foreach {Write-Host "$_"}
I have succeeded using some more complicated code but I think I can simplify it and accomplish most of the work through a couple lines of code(The code above accomplishes half of that).
Running this on a windows 10 machine powershell v5.1
I do things like this all the time. You don't need to use get-content.
ls -r *.pl | Select-String \w+\.pl
file.pl:1:file2.pl
You don't need to use ls or Get-ChildItem either; Select-String can take a path parameter:
Select-String -Pattern '\w+\.pl' -Path *.pl
which shortens to this in the shell:
sls \w+\.pl *.pl
(if your regex is more complex it might need spaces around it).
For the foreach {write-host part, you're writing a lot of code to turn useful objects back into less-useful strings, and forcibly writing them to the host instead of the standard output stream. You can pick out the data you want with:
sls \w+\.pl *.pl | select filename, {$_.matches[0]}
which will keep them as objects with properties, but render by default as a table.

Show hash of files in a directory

What is the easiest way to list all files in a directory with their hashes?
I am trying to compare a list of files in a folder. The problem is that all the file sizes are the same but I need to ensure that their content are the same too.
Powershell has a cmdlet named Get-FileHash. One can just do a ls and pipe the output to GetFileHash
eg. ls | Get-FileHash
You can also specify the hash algorithm by passing the -Algorithm parameter:
eg. ls | Get-FileHash -Algorithm MD5

Combining MD5 Analysis with Filename in single Output

I am struggling to combine the output from two commands into a single CSV / TXT file.
The first command is to recursively search a folder and create an MD5 number for each document. This is then exported to a CSV file that includes the full path.
dir -recurse | Get-FileHash -Algorithm MD5 | Export-CSV MD5ofFolder.csv
The second command is to retrieve all the filenames within the folder (and sub-folders) WITHOUT including any pathing:
get-childitem -recurse|foreach {$_.name} > filename.txt
In a perfect world, I would be able to export a single CSV or TXT document that contains the MD5 values, the full path, and the filename (with extension).
I note that my second code string also produces the folder names in the output, which is not desirable. I am able to produce a text output without the folder names, but the code is ugly, and it doesn't do what I want:
dir -recurse | Get-FileHash -Algorithm MD5 | dir -recurse | foreach {$_.name} > filename.txt
I am sure this is a simple problem for someone smarter than me, so any and all help would be appreciated - I am VERY new to PowerShell.
Add the name to the output from Get-FileHash with Select-Object and a calculated property:
dir -recurse |Get-FileHash -Algorithm MD5 |Select-Object Hash,Path,#{Name='Name';Expression={[System.IO.Path]::GetFileName($_.Path)}} |Export-Csv filename.csv
Now you have it all in a single csv

Unable to get output from get-filehash

I am looking for a reliable command-line method of getting SHA256 hashes for files in Windows. My understanding is that the way to do this is via Microsoft's Get-FileHash cmdlet under PowerShell. I have seen several web sites with examples and reviewed Microsoft's own documentation. It appears that the following syntax should work on Windows Server 2012:
Get-FileHash myfile.txt -Algorithm SHA256
The command runs without error, but there is no output. If I send the output to a file, the file is created with no content. I have also seen examples which pipe the output to Format-List; I tried that, but still nothing. I have also tried running the command with invalid arguments, and again nothing.
I am open to using a different program, but due to business requirements, it would need to be a supported download.
I'm using PowerShell 4.0 and I just encountered the same problem of null output from Get-FileHash. The cause of my problem is different than the OP but I have found a solution to my problem and I figured I would post my findings for anyone who came to this page trying to solve the problem of null output (or seemingly incorrect output) from Get-FileHash.
The problem only happens (for me) when the path to the target file contains brackets [ ] and those brackets contain either zero characters or 2 or more characters.
EDIT: I now understand WHY this happens. The string is interpreted as Regular Expression (RegEx) so the square brackets [ ] take on their special RegEx meaning. The -LiteralPath tells PowerShell to interpret the string as a simple match (no RegEx).
Consider the following paths which refer to 4 existing text files (hypothetically):
C:\Test\My Text.txt
C:\Test\My [Text].txt
C:\Test\My [Te]xt.txt
C:\Test\My Text[].txt
The following command produces normal output:
Get-FileHash "C:\Test\My Text.txt"
but there will be null output if using the following commands:
Get-FileHash "C:\Test\My [Text].txt"
Get-FileHash "C:\Test\My [Te]xt.txt"
Get-FileHash "C:\Test\My Text[].txt"
This can be solved by using the -LiteralPath switch. For example:
Get-FileHash -LiteralPath "C:\Test\My [Text].txt"
Variables are expanded normally when using the -LiteralPath switch. For example:
(Get-ChildItem C:\Test).FullName | ForEach {
Get-FileHash -LiteralPath $_
}
If there is exactly 1 character between the brackets, the brackets will be ignored when using Get-FileHash.
Consider the following paths which refer to 3 existing text files (hypothetically), each with unique hash values:
C:\Test\My Text.txt
C:\Test\My Tex[t].txt
C:\Test\My[ ]Text.txt
Get-FileHash interprets all three of the following commands in exactly the same way ( the path is interpreted as C:\Test\My Text.txt ) and therefore each command has the exact same output despite each file having it's own unique hash value:
Get-FileHash "C:\Test\My Text.txt"
Get-FileHash "C:\Test\My Tex[t].txt"
Get-FileHash "C:\Test\My[ ]Text.txt"
P.S. I'm a very new programmer, please forgive me for any poor usage of terminology.
Get-FileHash, requires Windows PowerShell 4.0
Based on your comments you are at version 3, which is default on Win 2012 (non R2) Here how to check you PS version
You can update PS on Win 2012 (non R2) to version 4.0 or use Win 2012 R2
If you just run Get-FileHash on a PS version 3 system you should get
PS C:\> Get-FileHash
Get-FileHash : The term 'Get-FileHash' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Get-FileHash
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-FileHash:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
On lower PS version you can use this function
Function Get-FileHashTSO([String] $FileName,$HashName = "SHA1")
{
$FileStream = New-Object System.IO.FileStream($FileName,[System.IO.FileMode]::Open)
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash($FileStream)|%{[Void]$StringBuilder.Append($_.ToString("x2"))}
$FileStream.Close()
$StringBuilder.ToString()
}
store it as .ps (e.g. Get-FileHashTSO.ps1) file and call it like this
powershell -command "& { . C:\myScripts\Get-FileHashTSO.ps1 ; Get-FileHashTSO "C:\someLocation\someFile.iso" "SHA1" }"
I calculated the hash of all files on a drive and exported them to a .csv by using this:
Get-ChildItem C: -Recurse |
Get-FileHash |
Export-Csv -Path C:\Users\yourname\Documents\Output\hashes.csv -NoTypeInformation
Import-Csv -Path C:\Users\yourname\Documents\Output\hashes.csv
Why it works (I think):
Get-ChildItem <--gets everything under your path, in this case C:, and -Recurse gets all the files within folders. You can add limitations if needed.
Get-FileHash <--after you've said get these files, you're saying calculate the hashes
Export-Csv <--says, we're sending your hashes out as comma separated values file, which is crazy helpful, and -Path says put it HERE, -NoTypeInformation just removes the #TYPE row from the top of the .csv file, and versions of PowerShell before 6 need this.
Import-Csv <--says, bring that data into the file at this -Path
Be sure to have the .csv file created in that location before you run the script. The script won't create the container for you. There's no need to clear the data from the .csv file between runs, it clears itself.
I'm not really a programmer, hence the annoyingly lengthy explanation. Hope it helps others. Wouldn't have figured it out without Stack Overflow forums! Thanks everyone!!