1

My aim is to compare two directories exactly - including the structure of the directories and sub-directories.

I need this, because I want to monitor if something in the folder E:\path2 was changed. For this case a copy of the full folder is in C:\path1. If someone changes something it has to be done in two directories.

It is important for us, because if something is changed in the directory (accidentally or not) it could break down other functions in our infrastructure.

This is the script I've already written:

# Compare files for "copy default folder"
# This Script compares the files and folders which are synced to every client.
# Source: https://mcpmag.com/articles/2016/04/14/contents-of-two-folders-with-powershell.aspx

# 1. Compare content and Name of every file recursively
$SourceDocsHash = Get-ChildItem -recurse –Path C:\path1 | foreach  {Get-FileHash –Path $_.FullName}
$DestDocsHash = Get-ChildItem -recurse –Path E:\path2 | foreach  {Get-FileHash –Path $_.FullName}
$ResultDocsHash = (Compare-Object -ReferenceObject $SourceDocsHash -DifferenceObject $DestDocsHash -Property hash -PassThru).Path

# 2. Compare name of every folder recursively
$SourceFolders = Get-ChildItem -recurse –Path C:\path1 #| where {!$_.PSIsContainer}
$DestFolders = Get-ChildItem -recurse –Path E:\path2 #| where {!$_.PSIsContainer}

$CompareFolders = Compare-Object -ReferenceObject $SourceFolders -DifferenceObject $DestFolders -PassThru -Property Name
$ResultFolders = $CompareFolders |  Select-Object FullName

# 3. Check if UNC-Path is reachable
# Source: https://stackoverflow.com/questions/8095638/how-do-i-negate-a-condition-in-powershell
# Printout, if UNC-Path is not available.
if(-Not (Test-Path \\bb-srv-025.ftscu.be\DIP$\Settings\ftsCube\default-folder-on-client\00_ftsCube)){
  $UNCpathReachable = "UNC-Path not reachable and maybe"
}

# 4. Count files for statistics
# Source: https://stackoverflow.com/questions/14714284/count-items-in-a-folder-with-powershell
$count = (Get-ChildItem -recurse –Path E:\path2 | Measure-Object ).Count;

# FINAL: Print out result for check_mk
if($ResultDocsHash -Or $ResultFolders -Or $UNCpathReachable){
  echo "2 copy-default-folders-C-00_ftsCube files-and-folders-count=$count CRITIAL - $UNCpathReachable the following files or folders has been changed: $ResultDocs $ResultFolders (none if empty after ':')"
}
else{
  echo "0 copy-default-folders-C-00_ftsCube files-and-folders-count=$count OK - no files has changed"
}

I know the output is not perfect formatted, but it's OK. :-)

This script spots the following changes successfully:

  • create new folder or new file
  • rename folder or file -> it is shown as error, but the output is empty. I can live with that. But maybe someone sees the reason. :-)
  • delete folder or file
  • change file content

This script does NOT spot the following changes:

  • move folder or file to other sub-folder. The script still says "everything OK"

I've been trying a lot of things, but could not solve this.

Does anyone can help me how the script can be extended to spot a moved folder or file?

Mike Sherrill 'Cat Recall'
  • 91,602
  • 17
  • 122
  • 185
  • 1
    Rather than chase down changes after the fact, would you consider making a script that must be used to put a file into the directory? The file submission script would put a copy of the file into both directories. Users could have read access to the directories, but the script would use credentials that would allow it to write into the directories. Alternatively, there could be a separate submission directory where users would put new/changed files. A script could watch the directory or periodically copy the submission directory file to other directories. – lit Jul 25 '17 at 15:41
  • Using the .NET FileSystemWatcher class might be a better choice. [From the TechNet gallery](https://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b). – Mike Sherrill 'Cat Recall' Jul 25 '17 at 15:57
  • @MikeSherrill'CatRecall' : That's interesting. I will try that and post a feedback. Maybe I have to clarify my aim a bit: I want to see if files and folders are changed in a specific directory. (E:\path2) My approach was to compare it with a reference directory. (C:\path1) Sorry for beeing complicate. – M. Nydegger Jul 25 '17 at 19:57
  • Well, the FileSystemWatcher sounds like the very thing you need. Among other things, it would eliminate the need for the reference directory, and it would simplify and reduce your code. – Mike Sherrill 'Cat Recall' Jul 25 '17 at 20:18

2 Answers2

1

I think your best bet is to use the .NET FileSystemWatcher class. It's not trivial to implement an advanced function that uses it, but I think it will simplify things for you.

I used the article Tracking Changes to a Folder Using PowerShell when I was learning this class. The author's code is below. I cleaned it up as little as I could stand. (That publishing platform's code formatting hurts my eyes.)

I think you want to run it like this.

New-FileSystemWatcher -Path E:\path2 -Recurse

I could be wrong.

Function New-FileSystemWatcher  {

    [cmdletbinding()]
    Param (

    [parameter()]
    [string]$Path,

    [parameter()]
    [ValidateSet('Changed', 'Created', 'Deleted', 'Renamed')]
    [string[]]$EventName,

    [parameter()]
    [string]$Filter,

    [parameter()]
    [System.IO.NotifyFilters]$NotifyFilter,

    [parameter()]
    [switch]$Recurse,

    [parameter()]
    [scriptblock]$Action

    )

    $FileSystemWatcher  = New-Object System.IO.FileSystemWatcher

    If (-NOT $PSBoundParameters.ContainsKey('Path')){
        $Path  = $PWD
    }

    $FileSystemWatcher.Path = $Path

    If ($PSBoundParameters.ContainsKey('Filter')) {
        $FileSystemWatcher.Filter = $Filter
    }

    If ($PSBoundParameters.ContainsKey('NotifyFilter')) {
        $FileSystemWatcher.NotifyFilter =  $NotifyFilter
    }

    If ($PSBoundParameters.ContainsKey('Recurse')) {
        $FileSystemWatcher.IncludeSubdirectories =  $True
    }

    If (-NOT $PSBoundParameters.ContainsKey('EventName')){
        $EventName  = 'Changed','Created','Deleted','Renamed'
    }

    If (-NOT $PSBoundParameters.ContainsKey('Action')){
        $Action  = {
            Switch  ($Event.SourceEventArgs.ChangeType) {
                'Renamed'  {
                    $Object  = "{0} was  {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath,
                    $Event.SourceEventArgs.ChangeType,
                    $Event.SourceArgs[-1].FullPath,
                    $Event.TimeGenerated
                }

                Default  {
                    $Object  = "{0} was  {1} at {2}" -f $Event.SourceEventArgs.FullPath,
                    $Event.SourceEventArgs.ChangeType,
                    $Event.TimeGenerated
                }
            }

            $WriteHostParams  = @{
                ForegroundColor = 'Green'
                BackgroundColor = 'Black'
                Object =  $Object
            }

            Write-Host  @WriteHostParams
        }

    }

    $ObjectEventParams  = @{
        InputObject =  $FileSystemWatcher
        Action =  $Action
    }

    ForEach  ($Item in  $EventName) {
        $ObjectEventParams.EventName = $Item
        $ObjectEventParams.SourceIdentifier =  "File.$($Item)"
        Write-Verbose  "Starting watcher for Event: $($Item)"
        $Null  = Register-ObjectEvent  @ObjectEventParams
    }

} 

I don't think any example I've found online tells you how to stop watching the filesystem. The simplest way is to just close your PowerShell window. But I always seem to have 15 tabs open in each of five PowerShell windows, and closing one of them is a nuisance.

Instead, you can use Get-Job to get the Id of registered events. Then use Unregister-Event -SubscriptionId n to, well, unregister the event, where 'n' represents the number(s) you find in the Id property of Get-Job..

Mike Sherrill 'Cat Recall'
  • 91,602
  • 17
  • 122
  • 185
0

So basically you want to synchronize the two folders and note all the changes made on that:

I would suggest you to use

Sync-Folder Script

Or

FreeFile Sync.

Ranadip Dutta
  • 8,857
  • 3
  • 29
  • 45
  • Thanks for your answer. I don't want to synchronise (write). Just compare (read). The aim is the output to check_mk to show differences and get an alarm. Is it possible to use a sychronise script, but stops after comparing and just "simulate" and get the output? – M. Nydegger Jul 25 '17 at 18:23