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

推荐订阅源

云风的 BLOG
云风的 BLOG
The Last Watchdog
The Last Watchdog
L
Lohrmann on Cybersecurity
P
Proofpoint News Feed
I
Intezer
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
S
Schneier on Security
罗磊的独立博客
AWS News Blog
AWS News Blog
S
Securelist
J
Java Code Geeks
月光博客
月光博客
V
Vulnerabilities – Threatpost
博客园 - 【当耐特】
有赞技术团队
有赞技术团队
G
GRAHAM CLULEY
Project Zero
Project Zero
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
The Hacker News
The Hacker News
Know Your Adversary
Know Your Adversary
The GitHub Blog
The GitHub Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Scott Helme
Scott Helme
博客园 - Franky
S
Security Affairs
Cyberwarzone
Cyberwarzone
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Forbes - Security
Forbes - Security
K
Kaspersky official blog
Martin Fowler
Martin Fowler
Schneier on Security
Schneier on Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
宝玉的分享
宝玉的分享
腾讯CDC
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
Google Developers Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
美团技术团队
MyScale Blog
MyScale Blog
L
LangChain Blog
V
V2EX
N
News | PayPal Newsroom
N
News and Events Feed by Topic
aimingoo的专栏
aimingoo的专栏
Blog — PlanetScale
Blog — PlanetScale
IT之家
IT之家

Nicksxs's Blog

记一个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的路由表和网关等信息 如何开启mysql的optimizer trace 浅析下mysql的索引的基数与可选择性 分享个加速github访问的方法 学习体验下Koupleless框架-浅析代码 学习体验下Koupleless框架 体验下nocodb这个神奇的系统 学习下MDC的机制 重新复习理解下java的类加载器 记录下ffmpeg命令 尝试学习理解下Claude Code的实现原理和细节 在Antigravity中对比体验Claude Sonnet 4.5模型 来看下我装备了5060TI显卡的gpt-oss模型表现 来看下google最新力作Antigravity的水平如何
使用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系统是比较方便的,
其他的系统可能需要再研究下