-
Notifications
You must be signed in to change notification settings - Fork 26
/
Watch-Directory.ps1
178 lines (158 loc) · 4.43 KB
/
Watch-Directory.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<#
.Synopsis
File change watcher and handler.
Author: Roman Kuzmin
.Description
The script watches for changed, created, deleted, and renamed files in the
given directories. On changes it invokes the specified command with change
info. It is a dictionary where keys are changed file paths, values are last
change types.
If the command is omitted then the script outputs change info as text.
The script works until it is forcedly stopped (Ctrl-C).
.Parameter Path
Specifies the watched directory paths.
.Parameter Command
Specifies the command to process changes.
It may be a script block or a command name.
.Parameter Filter
Simple and effective file system filter. Default *.*
.Parameter Include
Inclusion regular expression pattern applied after Filter.
.Parameter Exclude
Exclusion regular expression pattern applied after Filter.
.Parameter Recurse
Tells to watch files in subdirectories as well.
.Parameter TestSeconds
Time to sleep between checks for change events.
.Parameter WaitSeconds
Time to wait after the last change before processing.
.Link
https://github.com/nightroman/PowerShelf
#>
param(
[Parameter(Position = 1, Mandatory = 1)]
[string[]]$Path,
[Parameter(Position = 2)]
$Command,
[string]$Filter,
[string]$Include,
[string]$Exclude,
[switch]$Recurse,
[int]$TestSeconds = 5,
[int]$WaitSeconds = 5
)
trap { $PSCmdlet.ThrowTerminatingError($_) }
$Path = foreach ($_ in $Path) {
$_ = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($_)
if (!([System.IO.Directory]::Exists($_))) {
throw "Missing directory: $_"
}
$_
}
Add-Type @'
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text.RegularExpressions;
public class FileSystemWatcherHelper : IDisposable
{
public string[] Path;
public string Filter;
public bool Recurse;
public DateTime LastTime { get { return _lastTime; } }
public bool HasChanges { get { return _changes.Count > 0; } }
OrderedDictionary _changes = new OrderedDictionary();
readonly List<FileSystemWatcher> _watchers = new List<FileSystemWatcher>();
readonly object _lock = new object();
DateTime _lastTime;
Regex _include;
Regex _exclude;
public void Include(string pattern)
{
if (!string.IsNullOrEmpty(pattern))
_include = new Regex(pattern, RegexOptions.IgnoreCase);
}
public void Exclude(string pattern)
{
if (!string.IsNullOrEmpty(pattern))
_exclude = new Regex(pattern, RegexOptions.IgnoreCase);
}
public void Start()
{
foreach (string p in Path)
{
FileSystemWatcher watcher = new FileSystemWatcher(p);
_watchers.Add(watcher);
watcher.IncludeSubdirectories = Recurse;
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
if (!string.IsNullOrEmpty(Filter))
watcher.Filter = Filter;
watcher.Created += OnChanged;
watcher.Changed += OnChanged;
watcher.Deleted += OnChanged;
watcher.Renamed += OnChanged;
watcher.EnableRaisingEvents = true;
}
}
public object GetChanges()
{
lock (_lock)
{
object r = _changes;
_changes = new OrderedDictionary();
return r;
}
}
public void Dispose()
{
foreach (FileSystemWatcher watcher in _watchers)
watcher.Dispose();
_watchers.Clear();
_changes.Clear();
}
void OnChanged(object sender, FileSystemEventArgs e)
{
if (_include != null && !_include.IsMatch(e.Name))
return;
if (_exclude != null && _exclude.IsMatch(e.Name))
return;
lock (_lock)
{
_changes[e.FullPath] = e.ChangeType;
_lastTime = DateTime.Now;
}
}
}
'@
$watcher = New-Object FileSystemWatcherHelper
$watcher.Path = $Path
$watcher.Filter = $Filter
$watcher.Recurse = $Recurse
try { $watcher.Include($Include) } catch { throw "Parameter Include: $_" }
try { $watcher.Exclude($Exclude) } catch { throw "Parameter Exclude: $_" }
try {
$watcher.Start()
for () {
Start-Sleep -Seconds $TestSeconds
if (!$watcher.HasChanges) { continue }
if (([datetime]::Now - $watcher.LastTime).TotalSeconds -lt $WaitSeconds) { continue }
$changes = $watcher.GetChanges()
if ($Command) {
try {
& $Command $changes
}
catch {
"$($_.ToString())`r`n$($_.InvocationInfo.PositionMessage)"
}
}
else {
foreach ($kv in $changes.GetEnumerator()) {
"$($kv.Value) $($kv.Key)"
}
}
}
}
finally {
$watcher.Dispose()
}