准确比较目录 - 包括移动的文件

时间:2017-07-25 14:55:18

标签: powershell filesystemwatcher

我的目标是准确地比较两个目录 - 包括目录和子目录的结构。

我需要这个,因为我想监视文件夹E:\path2中的某些内容是否已更改。对于这种情况,完整文件夹的副本位于C:\path1中。如果有人改变某些东西,必须在两个目录中完成。

对我们来说这很重要,因为如果目录中的某些内容发生了变化(意外与否),它可能会破坏我们基础架构中的其他功能。

这是我写过的剧本:

# 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"
}

我知道输出格式不完美,但没关系。 : - )

此脚本成功发现以下更改:

  • 创建新文件夹或新文件
  • 重命名文件夹或文件 - >它显示为错误,但输出为空。我可以忍受这一点。但也许有人看到了原因。 : - )
  • 删除文件夹或文件
  • 更改文件内容

此脚本未发现以下更改:

  • 将文件夹或文件移至其他子文件夹。脚本仍然说“一切正常”

我一直在尝试很多事情,但无法解决这个问题。

是否有人可以帮助我如何扩展脚本以发现移动的文件夹或文件?

2 个答案:

答案 0 :(得分:0)

所以基本上你想要同步这两个文件夹并记下对它做出的所有更改:

我建议你使用

Sync-Folder Script

或者

FreeFile Sync

答案 1 :(得分:0)

我认为最好的办法是使用.NET FileSystemWatcher类。实现使用它的高级功能并不是一件容易的事,但我认为它会为你简化一些事情。

我在学习这门课时使用了文章Tracking Changes to a Folder Using PowerShell。作者的代码如下。我尽可能少地清理它。 (那个发布平台的代码格式会伤害我的眼睛。)

我想你想这样运行它。

New-FileSystemWatcher -Path E:\path2 -Recurse

我错了。

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
    }

} 

我不认为我在网上找到的任何示例都会告诉您如何停止观看文件系统。最简单的方法是关闭PowerShell窗口。但我似乎总是在五个PowerShell窗口中打开15个标签,关闭其中一个是令人讨厌的。

相反,您可以使用Get-Job来获取已注册事件的ID。然后使用Unregister-Event -SubscriptionId n来取消注册活动,其中' n'表示您在Get-Job的Id属性中找到的数字..