How To Write New Checksums Files To Alternate Location

I am using the following script to generate a checksum file for hundreds of files in several subdiretories. The files are generated as expected in those directories. However I cannot figure out how to tell the files to be written to a different location that with their original source file. example write the files to c:\checksum\projects.

I know this will be a simple question/answer for someone that uses powershell daily.

 

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”

Foreach ($file in $files)
{
$hash = (Get-FileHash -Path $file.fullname -Algorithm SHA256).hash
Add-Content -Path ($file.fullname + ‘.checksum’) -Value $hash
}

 

Are you wanting all the files in the same folder or are you wanting to also have their original directory structure under c:\checksum\projects?

This will dump all the checksum files in the c:\Checksum\Projects directory, so beware of name collisions.

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

Foreach ($file in $files)
{
    $hash = (Get-FileHash -Path $file.fullname -Algorithm SHA256).hash
    $fulldest = Join-Path $dest ($file.name + '.checksum')
    Add-Content -Path $fulldest -Value $hash
}

The previous example uses a foreach statement construct which will load everything into memory. It’s very fast, but if you have a large amount of files this could be an issue. The following example changes it to utilize the pipeline.

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

$files | ForEach-Object {

    $fulldest = Join-Path $dest ($_.name + '.checksum')
    (Get-FileHash -Path $_.fullname -Algorithm SHA256).hash | Add-Content -Path $fulldest

}

I hope this helps

Are you wanting all the files in the same folder or are you wanting to also have their original directory structure under c:\checksum\projects?

This will dump all the checksum files in the c:\Checksum\Projects directory, so beware of name collisions.

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

Foreach ($file in $files)
{
    $hash = (Get-FileHash -Path $file.fullname -Algorithm SHA256).hash
    $fulldest = Join-Path $dest ($file.name + '.checksum')
    Add-Content -Path $fulldest -Value $hash
}

The previous example uses a foreach statement construct which will load everything into memory. It’s very fast, but if you have a large amount of files this could be an issue. The following example changes it to utilize the pipeline.

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

$files | ForEach-Object {

    $fulldest = Join-Path $dest ($_.name + '.checksum')
    (Get-FileHash -Path $_.fullname -Algorithm SHA256).hash | Add-Content -Path $fulldest

}

or even

$dest = "c:\checksum\projects"

Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5” | ForEach-Object {

    $fulldest = Join-Path $dest ($_.name + '.checksum')
    (Get-FileHash -Path $_.fullname -Algorithm SHA256).hash | Add-Content -Path $fulldest

}

I hope this helps

Actually I should have specified that originally. The same directory structure to avoid the issue of collisions.

It’s not pretty, and I’m sure there are better ways to do it, but this works assuming the targets are on the C: drive. If not, replace C: with the appropriate letter.

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

Foreach ($file in $files)
{
    $path = ($file.fullname | split-path -Parent) -replace 'C:',''
    $newpath = join-path $dest $path
    $fulldest = Join-Path $newpath ($file.name + '.checksum')
    $hash = (Get-FileHash -Path $file.fullname -Algorithm SHA256).hash
    Add-Content -Path $fulldest -Value $hash
}

or

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

$files | ForEach-Object {

    $path = ($_.fullname | split-path -Parent) -replace 'C:',''
    $newpath = join-path $dest $path
    if(-not (test-path $newpath)){New-Item -Path $newpath -ItemType Directory | Out-Null}
    $fulldest = Join-Path $newpath ($_.name + '.checksum')
    (Get-FileHash -Path $_.fullname -Algorithm SHA256).hash | Add-Content -Path $fulldest

}

FANTASTIC! The last example works as I needed. I really appreciate it.

One last question. Is there a way to set the date/time of the newly created checksum file to the same date/time of the original file it was created from?

I bet there is. Check this out and see what you can come up with. If you hit a snag after giving it a shot, let us know.
https://www.ghacks.net/2017/10/09/how-to-edit-timestamps-with-windows-powershell/

Thanks for all of the help.

One least question. I have everything figured out except for one item. Can you provide me an example of how to skip writing the .checksum file if it already exists?

You don’t need to check that the file matches a specific criteria or is a certain age? If not, then we can test-path like we did for the folder and only if it’s not found would we calculate the hash and write it.

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

Foreach ($file in $files)
{
    $path = ($file.fullname | split-path -Parent) -replace 'C:',''
    $newpath = join-path $dest $path
    if(-not (test-path $newpath)){New-Item -Path $newpath -ItemType Directory | Out-Null}
    $fulldest = Join-Path $newpath ($file.name + '.checksum')
    if(-not(Test-Path $fulldest))
    {
        $hash = (Get-FileHash -Path $file.fullname -Algorithm SHA256).hash
        Add-Content -Path $fulldest -Value $hash
    }
}

or

$files = Get-childitem -Path c:\projects -recurse -include * -Exclude “*.md5”
$dest = "c:\checksum\projects"

$files | ForEach-Object {

    $path = ($_.fullname | split-path -Parent) -replace 'C:',''
    $newpath = join-path $dest $path
    if(-not (test-path $newpath)){New-Item -Path $newpath -ItemType Directory | Out-Null}
    $fulldest = Join-Path $newpath ($_.name + '.checksum')
    if(-not(Test-Path $fulldest))
    {
        (Get-FileHash -Path $_.fullname -Algorithm SHA256).hash | Add-Content -Path $fulldest
    }
}

I was over complicating it. Thank you for the quick response. Works perfectly!