Powershell フォルダー監視/バッチ実行

Powershell フォルダー監視/バッチ実行

私は Powershell を初めて使用します。必要な機能を果たすスクリプトを見つけました。

私が探しているのは、特定のファイル名/タイプのフォルダーを監視するスクリプトです。ファイル名/タイプに応じて、特定のバッチ ファイルを実行して、サーバー ユーティリティのコマンドを実行したいと考えています。

これを正確に実現するにはどうすればよいでしょうか。このスクリプトを見つけて、PDF などのファイルがフォルダーに配置されるたびに bat ファイルを起動する呼び出し項目を追加しました。ただし、フィルター処理して、ファイル名に応じて異なる bat ファイルを起動する必要があります。現在、フォルダーに配置されるファイルごとに bat ファイルが呼び出されますが、これは望ましくありません。私の知識は最小限で、危険な程度しか知りません。

XXXX.PDF、XXRX.PDF、XXLX.PDF がフォルダーにヒットした場合、XXXX.PDF がヒットしたかどうかを知る必要があり、XXXX.bat のみを実行します。XXRX.PDF がヒットした場合は、XXRX.BAT のみを実行します。などなど。

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = "D:\XXXX\XXXX"
    $watcher.Filter = "*.PDF"
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true 

### DEFINE ACTIONS AFTER A EVENT IS DETECTED

    $action = {Invoke-Item "D:\BATCH FILES\XXXXX.bat" -filter = "XXXXX.pdf"}    

### DECIDE WHICH EVENTS SHOULD BE WATCHED + SET CHECK FREQUENCY  
    $created = Register-ObjectEvent $watcher "Created" -Action $action
###    $changed = Register-ObjectEvent $watcher "Changed" -Action $action
###    $deleted = Register-ObjectEvent $watcher "Deleted" -Action $action
###    $renamed = Register-ObjectEvent $watcher "Renamed" -Action $action
    while ($true) {sleep 5}

答え1

FileWatchersには癖があり、ファイルの作成方法に応じて:

特定の状況では、1 つの作成イベントによって、コンポーネントによって処理される複数の Created イベントが生成されることにお気付きになる場合があります。たとえば、FileSystemWatcher コンポーネントを使用してディレクトリ内の新しいファイルの作成を監視し、メモ帳を使用してファイルを作成してテストすると、作成されたファイルが 1 つだけであるにもかかわらず、2 つの Created イベントが生成される可能性があります。これは、メモ帳が書き込みプロセス中に複数のファイル システム アクションを実行するためです。メモ帳は、ファイルの内容を作成し、次にファイル属性を作成するバッチでディスクに書き込みます。他のアプリケーションも同様に実行される場合があります。FileSystemWatcher はオペレーティング システムのアクティビティを監視するため、これらのアプリケーションによって発生するすべてのイベントが取得されます。

注: メモ帳は、他の興味深いイベント生成を引き起こすこともあります。たとえば、ChangeEventFilter を使用して属性の変更のみを監視するように指定し、メモ帳を使用して監視しているディレクトリ内のファイルに書き込むと、イベントが発生します。これは、この操作中にメモ帳がファイルのアーカイブ属性を更新するためです。

したがって、あなたの場合は、単純なディレクトリ比較を使用します。ディレクトリの変更を監視し、ファイルを実行するスクリプトを次に示します。このスクリプトを として保存しますMonitorAndExecute.ps1。次の引数を受け入れます。

  • パス:監視するフォルダー。指定しない場合は、現在のディレクトリが使用されます。
  • フィルター:一致するファイル拡張子。デフォルトは*、つまりすべてのファイルに一致します。
  • 走る:新しいファイルが見つかったときに実行するファイル拡張子。デフォルトは . ですbat
  • 再帰:ディレクトリを再帰的に処理するかどうか。デフォルトは false です。
  • 間隔:フォルダースキャン間のスリープ時間(秒)。デフォルトは5秒です。
  • 冗長:Write-Verboseスクリプトはメッセージを通じて何が起こっているかを伝えます。

例 (PowerShell コンソールから実行)。

*.pdfフォルダー内のファイルを監視しD:\XXXX\XXXX、再帰的に実行します。新しいファイルが見つかった場合は、同じベース名と拡張子を持つファイルを実行し*.bat、詳細を表示します。

.\MonitorAndExecute.ps1 -Path 'D:\XXXX\XXXX' -Filter '*.pdf' -Run 'bat' -Recurse -Interval 10 -Verbose

MonitorAndExecute.ps1脚本:

Param
(
    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [ValidateScript({
        if(!(Test-Path -LiteralPath $_ -PathType Container))
        {
            throw "Input folder doesn't exist: $_"
        }
        $true
    })]
    [ValidateNotNullOrEmpty()]
    [string]$Path = (Get-Location -PSProvider FileSystem).Path,

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [string]$Filter = '*',

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [string]$Run = 'bat',

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [switch]$Recurse,

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [int]$Interval = 5
)

# Scriptblock that gets list of files
$GetFileSet = {Get-ChildItem -LiteralPath $Path -Filter $Filter -Recurse:$Recurse | Where-Object {!($_.PSIsContainer)}}

Write-Verbose 'Getting initial list of files'
$OldFileSet = @(. $GetFileSet)
do
{
    Write-Verbose 'Getting new list of files'
    $NewFileSet = @(. $GetFileSet)

    Write-Verbose 'Comaparing two lists using file name and creation date'
    Compare-Object -ReferenceObject $OldFileSet -DifferenceObject $NewFileSet -Property Name, CreationTime -PassThru |
        # Select only new files
        Where-Object { $_.SideIndicator -eq '=>' } |
            # For each new file...
            ForEach-Object {
                Write-Verbose "Processing new file: $($_.FullName)"
                # Generate name for file to run
                $FileToRun = (Join-Path -Path (Split-Path -LiteralPath $_.FullName) -ChildPath ($_.BaseName + ".$Run"))

                # If file to run exists
                if(Test-Path -LiteralPath $FileToRun -PathType Leaf)
                {
                    Write-Verbose "Running file: $FileToRun"
                    &$FileToRun
                }
                else
                {
                    Write-Verbose "File to run not found: $FileToRun"
                }
            }

    Write-Verbose 'Setting current list of files as old for the next loop'
    $OldFileSet = $NewFileSet

    Write-Verbose "Sleeping for $Interval seconds..."
    Start-Sleep -Seconds $Interval
}
while($true)

関連情報