linux C++ 通知文件类型创建
linux C++ notify file type creation
我需要在 linux 的特定文件夹中监控特定文件类型(具有已知扩展名)的创建。我知道 inotify 只会监视现有文件。我理解正确吗?
是否有 inotify(或类似包)的替代方案允许我按文件类型监控文件创建?
编辑:
我需要的是通过掩码监视文件创建。我需要监控 *.json 的路径,同时忽略其他文件类型。
这听起来像是 inotify 的一个很好的用例。 man page 有一个很好的例子,很容易转移到你的问题。
这里有一个小程序,可以像
一样使用
$ myprog /tmp '*.o' '*.a'
查看目录 /tmp
以创建 *.o
和 *.a
文件。请注意,模式被引用以防止 shell 扩展。程序 运行 一直持续到被 SIGINT
中断(按 Ctrl + C)。
我正在使用 fnmatch
将创建的文件的名称与模式进行匹配,并为 SIGINT
安装一个设置全局标志的信号处理程序。
#include <assert.h>
#include <errno.h>
#include <fnmatch.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
我定义了一个小的预处理器宏来使用 GCC 的 __attribute__
扩展对齐缓冲区。稍后我们实际使用此宏时会详细介绍。
#ifdef __GNUC__
# define ALIGNAS(TYPE) __attribute__ ((aligned(__alignof__(TYPE))))
#else
# define ALIGNAS(TYPE) /* empty */
#endif
这是一个全局标志,我们将在信号处理程序中设置它以指示程序应该正常退出。
static volatile int interrupted = 0;
这是信号处理程序本身。
static void
interruption_handler(const int s)
{
if (s == SIGINT)
interrupted = 1;
}
这个函数是程序的主力。
static int
monitor_directory(const char *const directory,
const char *const *const patterns,
const size_t pattern_count)
{
int notifyfd = -1;
int watchfd = -1;
int ret = 0;
const char * errmsg = "unknown error";
首先,我们初始化inotify。 inotify_init
将 return 一个文件描述符,我们可以从中 read()
通知。我使用阻塞 I/O 所以 read()
将阻塞直到事件发生。
notifyfd = inotify_init();
if (notifyfd < 0)
{
errmsg = "inotify_init";
goto catch;
}
现在我们注册要观看的文件。在我们的例子中,我们想要监视单个目录 (directory
) 以创建新文件 (IN_CREATE
)。 returned 文件描述符可用于告知(如果事件发生)它属于哪个监视文件。但是因为无论如何我们只看一个文件(恰好是一个目录),我们真的不需要这个信息。
watchfd = inotify_add_watch(notifyfd, directory, IN_CREATE);
if (watchfd < 0)
{
errmsg = "inotify_add_watch";
goto catch;
}
现在一切都已正确设置,我们可以从通知文件描述符开始 read()
ing。
while (1)
{
事先不知道从 inotify 描述符调用 read
会读取多少数据,所以我们必须读入 char
缓冲区.我们希望正确对齐它,很难。有关此的更多评论,请参见手册页。
char buffer[sizeof(struct inotify_event) + NAME_MAX + 1] ALIGNAS(struct inotify_event);
const struct inotify_event * event_ptr;
read()
来自文件描述符。如果我们被打断,read()
将解除阻塞并且 return –1 就像发生错误时一样。
ssize_t count = read(notifyfd, buffer, sizeof(buffer));
if (count < 0)
{
if (interrupted)
goto finally;
errmsg = "read";
goto catch;
}
我们有一个新事件,处理它。
event_ptr = (const struct inotify_event *) buffer;
assert(event_ptr->wd == watchfd);
assert(event_ptr->mask & IN_CREATE);
if (event_ptr->len)
{
size_t i;
尝试将文件名与我们的每个模式进行匹配。
for (i = 0; i < pattern_count; ++i)
{
switch (fnmatch(patterns[i], event_ptr->name, FNM_PATHNAME))
{
case 0:
/* Your application logic here... */
if (printf("%s\n", event_ptr->name) < 0)
{
errmsg = "printf";
goto catch;
}
break;
case FNM_NOMATCH:
break;
default:
errmsg = "fnmatch";
goto catch;
}
}
}
}
最后,我们必须做一些清理工作。 close()
ing 由 inotify 创建的文件描述符将导致它释放任何关联的资源。
finally:
if (watchfd >= 0)
{
int status = close(watchfd);
watchfd = -1;
if (status < 0)
{
errmsg = "close(watchfd)";
goto catch;
}
}
if (notifyfd >= 0)
{
int status = close(notifyfd);
notifyfd = -1;
if (status < 0)
{
errmsg = "close(notifyfd)";
goto catch;
}
}
return ret;
catch:
if (errmsg && errno)
perror(errmsg);
ret = -1;
goto finally;
}
这就是我们将所有内容和 运行 程序连接在一起的方式。
int
main(const int argc, const char *const *const argv)
{
if (argc < 3)
{
fprintf(stderr, "usage: %s DIRECTORY PATTERN...\n", argv[0]);
return EXIT_FAILURE;
}
{
struct sigaction sa;
sa.sa_handler = interruption_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
}
if (monitor_directory(argv[1], argv + 2, argc - 2) < 0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
我需要在 linux 的特定文件夹中监控特定文件类型(具有已知扩展名)的创建。我知道 inotify 只会监视现有文件。我理解正确吗?
是否有 inotify(或类似包)的替代方案允许我按文件类型监控文件创建?
编辑: 我需要的是通过掩码监视文件创建。我需要监控 *.json 的路径,同时忽略其他文件类型。
这听起来像是 inotify 的一个很好的用例。 man page 有一个很好的例子,很容易转移到你的问题。
这里有一个小程序,可以像
一样使用$ myprog /tmp '*.o' '*.a'
查看目录 /tmp
以创建 *.o
和 *.a
文件。请注意,模式被引用以防止 shell 扩展。程序 运行 一直持续到被 SIGINT
中断(按 Ctrl + C)。
我正在使用 fnmatch
将创建的文件的名称与模式进行匹配,并为 SIGINT
安装一个设置全局标志的信号处理程序。
#include <assert.h>
#include <errno.h>
#include <fnmatch.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
我定义了一个小的预处理器宏来使用 GCC 的 __attribute__
扩展对齐缓冲区。稍后我们实际使用此宏时会详细介绍。
#ifdef __GNUC__
# define ALIGNAS(TYPE) __attribute__ ((aligned(__alignof__(TYPE))))
#else
# define ALIGNAS(TYPE) /* empty */
#endif
这是一个全局标志,我们将在信号处理程序中设置它以指示程序应该正常退出。
static volatile int interrupted = 0;
这是信号处理程序本身。
static void
interruption_handler(const int s)
{
if (s == SIGINT)
interrupted = 1;
}
这个函数是程序的主力。
static int
monitor_directory(const char *const directory,
const char *const *const patterns,
const size_t pattern_count)
{
int notifyfd = -1;
int watchfd = -1;
int ret = 0;
const char * errmsg = "unknown error";
首先,我们初始化inotify。 inotify_init
将 return 一个文件描述符,我们可以从中 read()
通知。我使用阻塞 I/O 所以 read()
将阻塞直到事件发生。
notifyfd = inotify_init();
if (notifyfd < 0)
{
errmsg = "inotify_init";
goto catch;
}
现在我们注册要观看的文件。在我们的例子中,我们想要监视单个目录 (directory
) 以创建新文件 (IN_CREATE
)。 returned 文件描述符可用于告知(如果事件发生)它属于哪个监视文件。但是因为无论如何我们只看一个文件(恰好是一个目录),我们真的不需要这个信息。
watchfd = inotify_add_watch(notifyfd, directory, IN_CREATE);
if (watchfd < 0)
{
errmsg = "inotify_add_watch";
goto catch;
}
现在一切都已正确设置,我们可以从通知文件描述符开始 read()
ing。
while (1)
{
事先不知道从 inotify 描述符调用 read
会读取多少数据,所以我们必须读入 char
缓冲区.我们希望正确对齐它,很难。有关此的更多评论,请参见手册页。
char buffer[sizeof(struct inotify_event) + NAME_MAX + 1] ALIGNAS(struct inotify_event);
const struct inotify_event * event_ptr;
read()
来自文件描述符。如果我们被打断,read()
将解除阻塞并且 return –1 就像发生错误时一样。
ssize_t count = read(notifyfd, buffer, sizeof(buffer));
if (count < 0)
{
if (interrupted)
goto finally;
errmsg = "read";
goto catch;
}
我们有一个新事件,处理它。
event_ptr = (const struct inotify_event *) buffer;
assert(event_ptr->wd == watchfd);
assert(event_ptr->mask & IN_CREATE);
if (event_ptr->len)
{
size_t i;
尝试将文件名与我们的每个模式进行匹配。
for (i = 0; i < pattern_count; ++i)
{
switch (fnmatch(patterns[i], event_ptr->name, FNM_PATHNAME))
{
case 0:
/* Your application logic here... */
if (printf("%s\n", event_ptr->name) < 0)
{
errmsg = "printf";
goto catch;
}
break;
case FNM_NOMATCH:
break;
default:
errmsg = "fnmatch";
goto catch;
}
}
}
}
最后,我们必须做一些清理工作。 close()
ing 由 inotify 创建的文件描述符将导致它释放任何关联的资源。
finally:
if (watchfd >= 0)
{
int status = close(watchfd);
watchfd = -1;
if (status < 0)
{
errmsg = "close(watchfd)";
goto catch;
}
}
if (notifyfd >= 0)
{
int status = close(notifyfd);
notifyfd = -1;
if (status < 0)
{
errmsg = "close(notifyfd)";
goto catch;
}
}
return ret;
catch:
if (errmsg && errno)
perror(errmsg);
ret = -1;
goto finally;
}
这就是我们将所有内容和 运行 程序连接在一起的方式。
int
main(const int argc, const char *const *const argv)
{
if (argc < 3)
{
fprintf(stderr, "usage: %s DIRECTORY PATTERN...\n", argv[0]);
return EXIT_FAILURE;
}
{
struct sigaction sa;
sa.sa_handler = interruption_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
}
if (monitor_directory(argv[1], argv + 2, argc - 2) < 0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}