惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Last Week in AI
Last Week in AI
D
DataBreaches.Net
腾讯CDC
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
A
About on SuperTechFans
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
罗磊的独立博客
月光博客
月光博客
MyScale Blog
MyScale Blog
U
Unit 42
Martin Fowler
Martin Fowler
Stack Overflow Blog
Stack Overflow Blog
T
Tailwind CSS Blog
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
G
Google Developers Blog
博客园 - 【当耐特】
D
Docker
I
InfoQ
雷峰网
雷峰网

Nicksxs's Blog

Pi Agent 源码解析(一):从调试环境到双层主循环 6GB显存能跑35B MoE吗:FreeToken极限配置、性能压榨与实操教程 聊聊 Java 的类加载机制一 从ASM到ByteKit再到Arthas:一条Java字节码增强链路 8GB显存如何跑35B:深入理解FreeToken的专家缓存与CPU-GPU协同 让chatgpt来给我讲解下deepseek harness的插件体系 来学习下pi agent的原理 如何在命令行中输出进度条 联想 G400 安装 Windows 7 折腾记录 记一个gitea推送失败的问题 学一下chrome的扩展开发 看下chrome的内置模型 从 app.test 到小锁:valet 本地 HTTPS 的完整链路 浅析一下jpeg图片格式及其来源 关于github拉取下载加速的另一个方式 关于适合什么模型,推荐下llmfit 看看目前本地能跑什么模型,使用llama.cpp 最近使用vibe coding的一些感悟 一些设计模式的记忆点 使用xiaomi mimo大模型api运行Hermes Agent 结合Obsidian的cli的一体化体验 开始尝试使用obsidian作为笔记软件 学习下大神的知识库 体验下微软开源的Markdown转换工具Markitdown 学习下git的worktree 一些架构师知识点的记录 解答一下关于traefik的一点疑惑 记录一下迁移服务器需要使用的一些命令 较早代iPhone更换新iPhone的一些小指南 如何查看mac的路由表和网关等信息
使用php的inotify扩展来监听文件变更
2026-05-17 · via Nicksxs's Blog

偶尔下载一些视频媒体的时候会想要对它进行压缩来缩小占用空间,
但是如果每次等下载完再去用软件压缩会比较麻烦
这里就找到了inotify扩展,可以监听文件变更
这里其实想吹一下小米的mimo2.5模型,因为要装php扩展,
我的是php7.4,网上搜了下有基于pecl,有的是基于源码
我就直接丢给配了小米模型的hermes agent来安装,直接就给我装好了
顺带再帮我写了个artisan命令

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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class InotifyWatchCommand extends Command
{





protected $signature = 'file:watch
{path : Directory to watch}
{--recursive : Watch subdirectories recursively}
{--event=* : Events to watch (create, modify, delete, move). Default: create,modify}
{--ext=* : Only watch files with these extensions (e.g., --ext=mp4 --ext=mkv)}
{--output= : Output file path for logging (optional)}
{--format=text : Output format: text or json}';






protected $description = 'Watch directory for file write events using inotify';




protected $eventMap = [
'create' => IN_CREATE,
'modify' => IN_MODIFY,
'delete' => IN_DELETE,
'move' => IN_MOVED_TO,
'close_write' => IN_CLOSE_WRITE,
'open' => IN_OPEN,
'access' => IN_ACCESS,
'attrib' => IN_ATTRIB,
];




protected $inotify;




protected $watchDescriptors = [];




protected $outputHandle;




protected $running = true;






public function handle()
{
$path = trim($this->argument('path'));
$recursive = $this->option('recursive');
$events = $this->option('event') ?: ['create', 'modify'];
$extensions = $this->option('ext');
$outputFile = $this->option('output');
$format = $this->option('format');


if (!is_dir($path)) {
$this->error("Directory does not exist: {$path}");
return 1;
}


if ($outputFile) {
$this->outputHandle = fopen($outputFile, 'a');
if (!$this->outputHandle) {
$this->error("Cannot open output file: {$outputFile}");
return 1;
}
}


$eventMask = 0;
foreach ($events as $event) {
if (!isset($this->eventMap[$event])) {
$this->error("Unknown event: {$event}. Available: " . implode(', ', array_keys($this->eventMap)));
return 1;
}
$eventMask |= $this->eventMap[$event];
}


$eventMask |= IN_CLOSE_WRITE;


$this->inotify = inotify_init();

if (!$this->inotify) {
$this->error("Failed to initialize inotify");
return 1;
}


stream_set_blocking($this->inotify, false);


if (function_exists('pcntl_signal')) {
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
}


$this->addWatch($path, $recursive);

$this->info("Watching: {$path}");
$this->info("Events: " . implode(', ', $events));
$this->info("Recursive: " . ($recursive ? 'yes' : 'no'));
if ($extensions) {
$this->info("Extensions: " . implode(', ', $extensions));
}
$this->info("Press Ctrl+C to stop");
$this->line('');


$checkInterval = 500000;

while ($this->running) {

if (function_exists('pcntl_signal_dispatch')) {
pcntl_signal_dispatch();
}

$events = inotify_read($this->inotify);

if ($events !== false && !empty($events)) {
foreach ($events as $event) {
$this->handleEvent($event, $extensions, $format);
}
}

usleep($checkInterval);
}


$this->cleanup();

return 0;
}




protected function addWatch(string $path, bool $recursive): void
{
$mask = IN_CREATE | IN_MODIFY | IN_DELETE | IN_MOVED_TO | IN_CLOSE_WRITE | IN_OPEN | IN_ATTRIB;
$wd = inotify_add_watch($this->inotify, $path, $mask);
$this->watchDescriptors[$wd] = $path;

if ($recursive) {
$items = scandir($path);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$fullPath = $path . DIRECTORY_SEPARATOR . $item;
if (is_dir($fullPath)) {
$this->addWatch($fullPath, true);
}
}
}
}




protected function handleEvent(array $event, array $extensions, string $format): void
{
$wd = $event['wd'];
$mask = $event['mask'];
$name = $event['name'] ?? '';
$cookie = $event['cookie'] ?? 0;


$dir = $this->watchDescriptors[$wd] ?? 'unknown';


if (empty($name)) {
return;
}


if (!empty($extensions)) {
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if (!in_array($ext, $extensions)) {
return;
}
}


$eventType = $this->getEventType($mask);


$filePath = $dir . DIRECTORY_SEPARATOR . $name;


$data = [
'timestamp' => date('Y-m-d H:i:s'),
'event' => $eventType,
'file' => $name,
'path' => $filePath,
'dir' => $dir,
'cookie' => $cookie,
];


if ($format === 'json') {
$output = json_encode($data, JSON_UNESCAPED_UNICODE) . "\n";
} else {
$output = $this->formatTextOutput($data);
}


$this->writeToConsole($eventType, $output);


if ($this->outputHandle) {
fwrite($this->outputHandle, $output);
}
}




protected function getEventType(int $mask): string
{
$types = [];

if ($mask & IN_CREATE) {
$types[] = 'CREATE';
}
if ($mask & IN_MODIFY) {
$types[] = 'MODIFY';
}
if ($mask & IN_DELETE) {
$types[] = 'DELETE';
}
if ($mask & IN_MOVED_TO) {
$types[] = 'MOVED_TO';
}
if ($mask & IN_CLOSE_WRITE) {
$types[] = 'CLOSE_WRITE';
}
if ($mask & IN_OPEN) {
$types[] = 'OPEN';
}
if ($mask & IN_ACCESS) {
$types[] = 'ACCESS';
}
if ($mask & IN_ATTRIB) {
$types[] = 'ATTRIB';
}

return implode('|', $types);
}




protected function formatTextOutput(array $data): string
{
return sprintf(
"[%s] %-12s %s (%s)\n",
$data['timestamp'],
$data['event'],
$data['file'],
$data['dir']
);
}




protected function writeToConsole(string $eventType, string $output): void
{
if (strpos($eventType, 'CREATE') !== false) {
$this->info(rtrim($output));
} elseif (strpos($eventType, 'DELETE') !== false) {
$this->error(rtrim($output));
} elseif (strpos($eventType, 'MODIFY') !== false || strpos($eventType, 'CLOSE_WRITE') !== false) {
$this->comment(rtrim($output));
} elseif (strpos($eventType, 'MOVED_TO') !== false) {
$this->warn(rtrim($output));
} else {
$this->line(rtrim($output));
}
}




public function handleSignal(int $signal): void
{
$this->running = false;
$this->line('');
$this->info("Received signal {$signal}, shutting down...");
}




protected function cleanup(): void
{
if ($this->outputHandle) {
fclose($this->outputHandle);
}


foreach ($this->watchDescriptors as $wd => $path) {
inotify_rm_watch($this->inotify, $wd);
}

if (is_resource($this->inotify)) {
fclose($this->inotify);
}

$this->info("Watch stopped.");
}
}

就能看到文件的变更

这个流程下来我都没有任何调整,都是一步到位了
这个因为没有准确的条件对比,可能是hermes也起了一定作用,还是说本身模型就很强
主要是监听了文件的创建,修改,关闭写入等,
首先是inotify_init函数 — 初始化 inotify 实例,类似于客户端
然后是inotify_add_watch 函数 — 添加监听到已初始化的 inotify 实例

这样针对文件系统有变更的话就会比较方便去做下一步处理,
比如压缩,移动归档等
之前最开始的处理方式是每次都遍历,感觉还是挺不科学的,
已有的文件会一遍遍被重复扫描
这个方法还有个局限性就是得在Linux系统里,
其他系统例如mac需要fs_usage这些工具才行
Windows里需要使用 ReadDirectoryChangesW 等api
总结下来这个方式对于Linux系统下的ext4系统是比较方便的,
其他的系统可能需要再研究下