若有个需求:要求记录下某个目录下的所有操作,包括创建文件、修改文件、重命名、删除文件的操作,将以上所有的操作全部记录到日志中,或者做其他操作。.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
}
}
}
测试:
日志结果:
【注意】
以上代码存在一个缺陷,当文件在不同的目录下移动时,会有异常,只有修改一下代码逻辑便可实现。