使用System.IO.FileSystemWatcher

时间:2016-09-08 14:30:30

标签: powershell filesystemwatcher

需要一些PowerShell脚本的帮助,该脚本会监视文件夹以创建扩展名为.csv的新文件 检测到创建新文件时,会执行以下操作...

  1. 文件的详细信息将发送到日志文件
  2. 该文件的副本将发送到名为“1_ToBeProcessed \ MyImport.csv”的另一个文件夹(注意该文件已重命名为MyImport.csv)
  3. 将文件移至“\ MYSERVER \ MYCOMPANY \ MYFOLDER \ MyHistory”目录(原始文件名)
  4. 调用表达式来运行.bat文件,该文件将获取位于“\ MYSERVER \ MYCOMPANY \ MYFOLDER \ 1_ToBeProcessed”中的文件,并将该文件导入应用程序。
  5. 然后,脚本会休眠60秒,以便在处理下一个文件之前完成导入。 除非在脚本运行时创建另一个文件,否则此脚本都可以正常工作 同一类型的多个文件可能同时命中监视文件夹。因此,脚本可以循环遍历五个文件,然后另一个文件可以进入目录。当发生这种情况时,似乎脚本重新开始并开始循环遍历文件......但它会一遍又一遍地循环相同的文件。 我希望有人可以指出我正确的方向,我在这个脚本上做错了什么。由于添加了新文件,如何让它在重新启动之前完成当前循环中正在处理的文件? 我们将非常感谢您提供的任何帮助!
  6. 这就是我现在所拥有的......

    #Define watcher; Set path to watch; Set filter on file type
    $filewatcher = New-Object System.IO.FileSystemWatcher
    $filewatcher.Path = "\\MYSERVER\MYCOMPANY\MYFOLDER"
    $filewatcher.Filter = "*.csv"                     
    
    #Define "move to" and "copy to" paths
    $moveTo = "\\MYSERVER\MYCOMPANY\MYFOLDER\MyHistory"
    $copyTo = "\\MYSERVER\MYCOMPANY\MYFOLDER\1_ToBeProcessed\MyImport.csv"
    
    
    #Define actions after an Event is Detected
    $action = {$files = Get-ChildItem -Path $filewatcher.Path -Filter filewatcher.Filter 
            foreach ($file in $files)
            {
            #Define variables for log file
            $changeType = $Event.SourceEventArgs.ChangeType
            $logline = "$(Get-Date), $changeType, $file"
            #Actions to take
            Add-Content "\\MYSERVER\MYCOMPANY\MYFOLDER\3_Log\MyImportLog.txt" -value $logline
            Copy-Item $file.FullName -Destination $copyTo
            Move-Item $file.FullName -Destination $moveTo -Force
            Invoke-Expression "& '\\MYSERVER\MYCOMPANY\MYFOLDER\4_Scripts\PSscriptToRunBATfile.ps1'"
            #Pause the script for 60 seconds to allow it to finish posting the import before going to the next record
            Start-Sleep -Seconds 60
            }
            }
    
    #Decide which events should be watched and set check frequency 
    $onCreated = Register-ObjectEvent -InputObject $filewatcher -EventName "Created" -SourceIdentifier FileCreated -Action $action
    

2 个答案:

答案 0 :(得分:3)

我建议不要循环,而是在创建/更改文件时处理它们。也许如果你有很多文件开头,你可以:

(a)首先循环(仅一次循环)。然后启用文件观察程序,以便在此之后对文件进行更改。

(b)启用文件观察器,使用FileChanged& FileCreated,然后“触摸”所有文件以触发您的代码。

# based on the script by By BigTeddy 05 September 2011
# simplified by Andy Myatt, to use one script block
# https://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b

param(
    [string]$folderToWatch = "D:\Temp"
  , [string]$filter        = "*.*"
  , [string]$logFile       = "D:\Temp\Temp2\filewatcher.log"
)

# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folderToWatch, $filter -Property @{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}

# This script block is used/called by all 3 events and:
# appends the event to a log file, as well as reporting the event back to the console
$scriptBlock = {

  # REPLACE THIS SECTION WITH YOUR PROCESSING CODE
  $logFile = $event.MessageData # message data is how we pass in an argument to the event script block
  $name = $Event.SourceEventArgs.Name
  $changeType = $Event.SourceEventArgs.ChangeType
  $timeStamp = $Event.TimeGenerated
  Write-Host "$timeStamp|$changeType|'$name'" -fore green
  Out-File -FilePath $logFile -Append -InputObject "$timeStamp|$changeType|'$name'"
  # REPLACE THIS SECTION WITH YOUR PROCESSING CODE

}

# Here, all three events are registered.  You need only subscribe to events that you need:
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -MessageData $logFile -Action $scriptBlock
Register-ObjectEvent $fsw Deleted -SourceIdentifier FileDeleted -MessageData $logFile -Action $scriptBlock
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -MessageData $logFile -Action $scriptBlock

# To stop the monitoring, run the following commands:
#  Unregister-Event FileDeleted  ;  Unregister-Event FileCreated  ;  Unregister-Event FileChanged


#This script uses the .NET FileSystemWatcher class to monitor file events in folder(s).
#The advantage of this method over using WMI eventing is that this can monitor sub-folders.
#The -Action parameter can contain any valid Powershell commands.
#The script can be set to a wildcard filter, and IncludeSubdirectories can be changed to $true.
#You need not subscribe to all three types of event.  All three are shown for example.

答案 1 :(得分:1)

我认为您可能会遇到某种文件锁定问题,但如果不知道PSscriptToRunBATfile.ps1文件中的内容,则很难说清楚。

您的filewatcher将在NotifyEvents的默认LastWrite | FileName | DirectoryName上触发,并在此时循环目录。但是,您的get-childitem命令不一定按照复制它们的顺序返回文件。

更好的解决方案是让您根据$Event对象执行操作并更改PS1文件,将导致事件的文件作为输入参数:

$action = {$file = $Event.SourceEventArgs.FullPath
  #Define variables for log file
  $changeType = $Event.SourceEventArgs.ChangeType
  $logline = "$(Get-Date), $changeType, $file"
  #Actions to take
  Add-Content "\\MYSERVER\MYCOMPANY\MYFOLDER\3_Log\MyImportLog.txt" -value $logline
  Copy-Item $file -Destination $copyTo
  Move-Item $file -Destination $moveTo -Force
  Invoke-Expression "& '\\MYSERVER\MYCOMPANY\MYFOLDER\4_Scripts\PSscriptToRunBATfile.ps1' -InputFile $file"
}

注意:您可能希望将InternalBufferSize提高到默认值8k以上,这样您就不会错过任何更改:

    $filewatcher.InternalBufferSize = 32kb