监控目录下的文件操作与实时同步
若有个需求:要求记录下某个目录下的所有操作,包括创建文件、修改文件、重命名、删除文件的操作,将以上所有的操作全部记录到日志中,或者做其他操作。.NET提供了一个方法叫做“System.IO.FileSystemWatcher”,方便powershell来调用。具体使用方法,我慢慢解释。
一、监控某个目录下的所有操作
方案一:
$folder = "c:\test" #定义要监控哪个目录 $timeout = 1000 #设置监控的时间间隔 $filesystemwatcher = new-object System.IO.FileSystemWatcher $folder #使用FileSystemWatcher方法开启文件监控
echo "ctrl+c will exit" while ($true) { $result = $filesystemwatcher.WaitForChanged("all",$timeout) #监控种类,all代表监控所有操作,$timeout表示监控的时间间隔 if($result.TimedOut -eq $false) #$result.TimedOut返回true或false,若目录下不操作返回true,
{ $time = date -UFormat "%Y-%m-%d %H-%M-%S" $type = $result.ChangeType $name = $result.Name $oldname = $result.OldName echo "$time $type $name $oldname " #显示如下信息,时间、 操作的类型、操作文件的名称、操作文件原来的名称,只有重命名后才会显示 } }
测试:
在c:\test目录下增删改查一些文件
终端返回的结果:
在所执行的终端执行ctrl+c或者关闭终端即可终止监控程序。
方案二:
该方案与方案一不同的一点是,方案二会在后台运行,但方案一由while循环运行。
$watcher = New-Object System.IO.FileSystemWatcher #启动监控实例 $watcher.Path = "c:\test\" #监控的路径 $watcher.IncludeSubdirectories = $true #是否监控子目录下的文件操作 $watcher.EnableRaisingEvents = $true #默认是true
#重写changed、created、deleted、renamed方法 $changed = Register-ObjectEvent $watcher "Changed" -Action { $time = date -UFormat "%Y-%m-%d %H-%M-%S" write-host " $time Changed: $($eventArgs.FullPath)" } $created = Register-ObjectEvent $watcher "Created" -Action { $time = date -UFormat "%Y-%m-%d %H-%M-%S" write-host "$time Created: $($eventArgs.FullPath)" } $deleted = Register-ObjectEvent $watcher "Deleted" -Action { $time = date -UFormat "%Y-%m-%d %H-%M-%S" write-host "$time Deleted: $($eventArgs.FullPath)" } $renamed = Register-ObjectEvent $watcher "Renamed" -Action { $time = date -UFormat "%Y-%m-%d %H-%M-%S" write-host "$time Renamed: $($eventArgs.FullPath)" }
测试:
在被监控的目录下增删改查操作
返回的结果:
但是方案二退出与方案一的不一致。
1、退出当前执行的终端,可以退出整个监控程序
2、手动注销事件,注销以上重新的方法,需要在程序运行的终端执行
Unregister-Event $changed.Id Unregister-Event $created.Id Unregister-Event $deleted.Id Unregister-Event $renamed.Id
红框内的内容是输入进去的。
二、实时同步
有了目录监控的能力,可以扩展一下,实现文件实时同步。
有两个目录,目录A中凡是有了任何操作,便同步到B目录中,实现实时同步。具体代码如下:
【注意】
代码中监听操作的时间隔是1000ms,若两相邻操的时间间隔作小于1000ms时,就会出现不同步的问题
$folder = "\\share-server\t" #监听的源目录 $timeout = 1000 #时间间隔,ms $filesystemwatcher = new-object System.IO.FileSystemWatcher $folder $filesystemwatcher.IncludeSubdirectories = $true #开启监听子目录的功能 $des = "c:\test" #同步的目标目录 $logpath = "c:\test\logs\a.log" #日志记录的位置,需要提前创建好日志的父目录 echo "ctrl+c will exit" cp -Force -Recurse $folder\* $des #先将源目录中的所有文件拷贝到子目录中 while ($true) #开启监听 { $result = $filesystemwatcher.WaitForChanged("all",$timeout) if($result.TimedOut -eq $false) { $time = date -UFormat "%Y-%m-%d %H:%M:%S" $type = $result.ChangeType $name = $result.Name $oldname = $result.OldName echo "$time $type $name oldname:$oldname " >> $logpath #输出日志 $isfile = $true $filefullpath = $folder + "\" + $name #源文件的全路径 $dstpath = $des + "\" + $name #目标文件的全路劲 if( Test-Path $filefullpath ){ $filepath = $filefullpath } #当删除时,源文件的全路径不存在,需要借助目标文件的全路径来判断是文件还是目录 if( Test-Path $dstpath ){ $filepath = $dstpath } if ( (ls $filepath) -is [IO.fileinfo]) #判断是文件还是目录 { $isfile = $true } else { $isfile = $false } #根据不同的操作类型做出不同的操作 if( $type -eq "Changed" ) { if( $isfile ) { cp $filefullpath $dstpath echo "cp $filefullpath $dstpath" >> $logpath } } if( $type -eq "Created" ) { if( $isfile ) { New-Item $dstpath echo "cp $filefullpath $des" >> $logpath } else { mkdir $dstpath echo "mkdir $dstpath" >> $logpath } } if( $type -eq "Deleted" ) { rm -Recurse -Force $dstpath echo "rm $dstpath" >> $logpath } if( $type -eq "Renamed" ) { cd $des mv $oldname $name echo "mv $oldname $name" >> $logpath } } }
测试:
日志结果:
【注意】
以上代码存在一个缺陷,当文件在不同的目录下移动时,会有异常,只有修改一下代码逻辑便可实现。