Wednesday, June 21, 2017

How to compare two folders in PowerShell by excluding some folders from source or destination

Today I  just came across a scenario where which I need bit to exclude some folder inside my parent folders while doing a comparison. I know there is  - Exclude for Get-ChildItem , but unfortunately this seems not working good for folders /file together and my scenario.

Finally  found a way to do this by using the below code, which exclude any folders in the regular expression.

Assume we have a folder  structure as below and we want to compare  “C:\ Source” and “D:\ Destination” recursively by excluding the files in D:\ XXXXXX and D:\ YYYYYY with any difference in XXXXXX.txt to be picked up.

C:
|Source
          |-XXXXXX
          |                 |File1.txt
          |                 |File2.txt
          |                 |File3.txt
          |-FolderA
          |                 |File4.txt
          |                 |File5.txt
          |                 |File6.txt
          |-FolderB
          |                 |File1.txt
          |                 |File2.txt
          |                 |File3.txt
          |- YYYYYY
          |                 |File1.txt
          |                 |File2.txt
          |                 |File3.txt
          |- XXXXXX.txt

 
D:
|Destination
          |- XXXXXX
          |                 |File1.txt
          |                 |File2.txt
          |                 |File3.txt
          |                 |File4.txt
          |-FolderA
          |                 |File4.txt
          |                 |File5.txt
          |                 |File6.txt
          |-FolderB
          |                 |File1.txt
          |                 |File2.txt
          |                 |File3.txt
          |- YYYYYY
          |                 |File1.txt
          |                 |File2.txt
          |                 |File3.txt
          |                 |File4.txt
          |- XXXXXX.txt

Below  code will fulfill the requirements. This will compare the source and destination folders by excluding any folders with name XXXXXX and YYYYYY. But this will also pick up any changes to files with same name in regular Expression Eg  XXXXXX.txt or YYYYYY.txt

Clear

#Arrive at the hash of destination folder by excluding the folders in regular expression "\w*\\XXXXXX\\\w*|\w*\\YYYYYY\\\w*"
$hashDestination=Get-ChildItem -Path "D:\temp\Destination" -Recurse | where {$_.FullName -notmatch  "\w*\\XXXXXX\\\w*|\w*\\YYYYYY\\\w*"  } |Get-FileHash -Algorithm "SHA256"

#Arrive at the hash of Source folder by excluding the folders in regular expression "\w*\\XXXXXX\\\w*|\w*\\YYYYYY\\\w*"
$hashSource=Get-ChildItem -Path "D:\temp\Source" -Recurse | where {$_.FullName -notmatch  "\w*\\XXXXXX\\\w*|\w*\\YYYYYY\\\w*"  } |Get-FileHash -Algorithm "SHA256"

$diff = 0
#Compare the hash we got  and increment $diff if we have a difference
Compare-Object -ReferenceObject $hashSource.Hash -DifferenceObject $hashDestination.Hash | % { If ($_.Sideindicator -ne " ==") {$diff+=1} }

#Check if $diff not equal to zero
If($diff -ne 0)
{
    #If yes, we have a difference
    return $true
}
else
{
    #If yes, we have a difference
    return $false
}

A much  mature version of the same logic can be found below. Which encapsulate the logic in reusable function Compare-Folders

clear
function Compare-Folders
{
    param(
        #Source Path Eg: "D:\temp\Source"
        $Source,
        #Destination Path Eg: "D:\temp\Destination"
        $Destination,
        #Regular Expression for Folder Exclusion Eg:"\w*\\YYYYYY\\\w*|\w*\\XXXXXX\\\w*"
        $ExcludeList
    )

    #Arrive at the hash of Source folder by excluding the folders in regular expression "\w*\\XXXXXX\\\w*|\w*\\YYYYYY\\\w*"
    $hashSource = Get-ChildItem -Path  $Source -Recurse | where {$_.FullName -notmatch  $ExcludeList  } |Get-FileHash -Algorithm "SHA256"

    #Arrive at the hash of destination folder by excluding the folders in regular expression "\w*\\XXXXXX\\\w*|\w*\\YYYYYY\\\w*"
    $hashDestination  = Get-ChildItem -Path  $Destination -Recurse | where {$_.FullName -notmatch  $ExcludeList  } |Get-FileHash -Algorithm "SHA256"

    $diff = 0
    #Compare the hash we got  and increment $diff if we have a difference
    Compare-Object -ReferenceObject $hashSource.Hash -DifferenceObject $hashDestination.Hash | % { If ($_.Sideindicator -ne " ==") {$diff+=1} }

    #Check if $diff not equal to zero
    If($diff -ne 0)
    {
        #If yes, we have a difference
        return $true
    }
    else
    {
        #If yes, we have a difference
        return $false
    }
}

 
$SourceFolder="D:\temp\Source"
$DestinationFolder="D:\temp\Destination"
#Compare the folders using the function
if(Compare-Folders -Source $SourceFolder -Destination $DestinationFolder -ExcludeList "\w*\\YYYYYY\\\w*|\w*\\XXXXXX\\\w*")
{
    Write-Host "Changes Found"
}
else
{
    Write-Host "No Changes Found"
}

Tuesday, June 20, 2017

Executing Parallel tasks in Power-Shell……………

There can be situation in PowerShell when we want to execute multiple blocks of code in parallel , wait for the block to complete execution and then proceed further executing rest code. This can be done in PowerShell using the command Start-Job  and Wait-Job

Below code block will show how you can accomplish the same. We can ask PowerShell to execute a code block in parallel using the Start-Job commandlet. The Start-Job commandlet  accepts the code block to be executed in parallel as value for -ScriptBlock  and the parameters to the block can be passed as values for - ArgumentList

This code tries to write lines to a common files stored at d:\Wait-Job_Test_Log.txt from two jobs Job1 and Job2

clear

$name = "Rajesh"
$name2 = "Renjith"
 
#Starting Job1 and passing $name,$name2 as arguments to Script block
Start-Job -Name Job1 -ArgumentList $name,$name2 -ScriptBlock {

  param ($name1,$name2)

  #Just adding some delay

  Start-Sleep -s 10

  #Add content to file

  Add-Content -Value "Job1 Started....." -Path "d:\Wait-Job_Test_Log.txt"

  #Add content to file

  Add-Content -Value "Hello $name1 I am from Job1" -Path "d:\Wait-Job_Test_Log.txt"

  #Add content to file

  Add-Content -Value "Hello $name2 I am from Job1"-Path "d:\Wait-Job_Test_Log.txt"
}

#Starting Job2
Start-Job -Name Job2 -ScriptBlock {

  #Just adding some delay

  Start-Sleep -s 20

  #Add content to file

  Add-Content -Value "Job2 Started....." -Path "d:\Wait-Job_Test_Log.txt"

  #Add content to file

  Add-Content -Value "I am From Job2" -Path "d:\Wait-Job_Test_Log.txt"
}

#Add content to file
Add-Content -Value "I am from Outside, waiting for job1,job2 to finish" -Path "d:\Wait-Job_Test_Log.txt"


#Check if a Job with name Job1 has already been started,

if([bool](get-job -Name Job1 -ea silentlycontinue) )
{

  #if Yes, wait for the jon completed
  Wait-Job Job1

  #Add content to file
  Add-Content -Value "Job1 Finished....." -Path "d:\Wait-Job_Test_Log.txt"
}

#Check if a Job with name Job1 has already been started,
if([bool](get-job -Name Job2 -ea silentlycontinue) )
{

  #if Yes, wait for the jon completed
  Wait-Job Job2

  #Add content to file
  Add-Content -Value "Job2 Finished....." -Path "d:\Wait-Job_Test_Log.txt"
}

#Add content to file
Add-Content -Value "I am from Outside after Jobs completed" -Path "d:\Wait-Job_Test_Log.txt"
 
#Add content to file

Add-Content -Value "I am from Outside after Jobs completed" -Path "d:\Wait-Job_Test_Log.txt"