【问题标题】:linux C++ notify file type creationlinux C++ 通知文件类型创建
【发布时间】:2015-02-11 13:22:13
【问题描述】:

我需要在 linux 的特定文件夹中监视特定文件类型(具有已知扩展名)的创建。我知道 inotify 只会查看现有文件。我理解正确吗?

是否有替代 inotify(或类似包)的方法可以让我按文件类型监控文件创建?

编辑: 我需要的是通过掩码监视文件创建。我需要监控 *.json 的路径,而忽略其他文件类型。

【问题讨论】:

  • inotify 很棒,当然还有一些库可以让它更容易使用。
  • Inotify 确实只会监控现有文件,因为监控不存在的东西是不可能的。但是,您忘记了您也可以监视 目录
  • 关于“文件类型”,Linux(或通常在 POSIX/UNIX 世界中)没有真正不同的文件“类型”。但是,如果您监视目录中是否有新文件,则可以检查文件扩展名以查看它是否是您想要的。
  • @JoachimPileborg 实际上可以在目录上监视文件创建,它还为您提供创建的文件名,我有一个小程序可以做到这一点,我写了一个来帮助我另一个软件中的错误。
  • 我认为到目前为止,您的 cmets 联盟很好地回答了这个问题。你考虑过让他们回答吗?我肯定会赞成它。别人现在写答案有点困难,因为它闻起来有点像抄袭。

标签: c++ c linux inotify


【解决方案1】:

这听起来像是 inotify 的一个很好的用例。 man page 有一个很好的例子,很容易转移到你的问题上。

这是一个小程序,可以像这样使用

$ myprog /tmp '*.o' '*.a'

查看目录/tmp 以创建*.o*.a 文件。请注意,引用模式是为了防止外壳扩展。程序永远运行,直到被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";

首先,我们初始化inotifyinotify_init 将返回一个文件描述符,我们可以从中获得 read() 通知。我使用阻塞 I/O,所以 read() 将阻塞直到事件发生。

  notifyfd = inotify_init();
  if (notifyfd < 0)
    {
      errmsg = "inotify_init";
      goto catch;
    }

现在我们注册要观看的文件。在我们的例子中,我们希望观察一个目录 (directory) 以创建新文件 (IN_CREATE)。返回的文件描述符可用于告诉(如果发生事件)它属于哪个监视文件。但由于我们只观察一个文件(恰好是一个目录),所以我们并不需要这些信息。

  watchfd = inotify_add_watch(notifyfd, directory, IN_CREATE);
  if (watchfd < 0)
    {
      errmsg = "inotify_add_watch";
      goto catch;
    }

现在一切都设置好了,我们可以从通知文件描述符开始read()ing。

  while (1)
    {

事先不知道从 inotify 描述符调用read 会读取多少,所以我们必须读入char 缓冲区。我们希望适当地调整它,艰难的。有关此内容的更多 cmets,请参见手册页。

      char buffer[sizeof(struct inotify_event) + NAME_MAX + 1] ALIGNAS(struct inotify_event);
      const struct inotify_event * event_ptr;

read() 来自文件描述符。如果我们被打断,read() 将解除阻塞并返回 –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;
}

【讨论】:

  • 这是一个方便的示例代码,除了 C++ 标签 - catch 是一个关键字。
  • 哇。非常感谢你。我会检查一下。
  • @Petesh 我决定编写一个更通用的 C 示例程序。 (问题标记为cc++。)我习惯于在C 中使用catch 作为标签,以明确我使用goto 来模拟异常处理。是的,这意味着代码不会编译为 C++ 代码,但我认为这更像是一个特性而不是错误,因为它提醒我们不要在 C++ 中使用 goto 错误处理。但是,如果您需要双语言支持,重命名标签将是一个小修改。
  • @5gon12eder 为什么你需要 main 上的 struct sigaction 块?没有它,一切似乎都正常。
  • @mousomer 用于确保收到 SIGINT 时有序关机。在实际程序中,您可以使用适合您的应用程序的任何主循环。如果您不安装处理程序,默认情况下 SIGINT 将终止您的应用程序而不进行清理。
猜你喜欢
  • 2011-12-28
  • 2011-07-25
  • 2018-06-12
  • 2018-06-09
  • 2020-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多