【问题标题】:How to skip a file when scanning files in a directory扫描目录中的文件时如何跳过文件
【发布时间】:2018-05-16 19:09:48
【问题描述】:

我有一个列出目录中所有文件的程序。如果文件链接断开,我想跳过该文件并继续扫描文件中的其他文件。如果有人能指出我错的地方,我将不胜感激。以下是我的代码的一部分

d = opendir(".");
while((dir = readdir(d)) != NULL) {

 char buff[256];
 int target = readlink (dir->d_name, buff, sizeof(buff));
    if (target == -1)
    {
        printf("i found broken link  so continuing to next file..\n");
        continue;
    }
}

我的问题是,它正在打印,而我只有一个损坏的链接

i found broken link  so continuing to next file
i found broken link  so continuing to next file
i found broken link  so continuing to next file

一直持续到最后一个文件。

【问题讨论】:

  • ... On error, -1 is returned and errno is set to indicate the error.... -->> EINVAL The named file is not a symbolic link.

标签: c


【解决方案1】:

您的问题应该有一些MCVE。另见inode(7)symlink(7)。阅读Advanced Linux Programming 或更新的内容。

考虑使用nftw(3)fts(3)(如果您需要递归扫描子目录)或至少对文件路径做一个stat(2)(因为您正在扫描当前目录,所以您不需要构造该文件路径)。记得跳过... 的条目;所以试试吧

 d = opendir(".");
 while((dir = readdir(d)) != NULL) {
   struct stat mystat;
   if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
   memset (&mystat, 0, sizeof(mystat));
   if (stat(dir->d_name, &mystat) ||  S_ISLNK(mystat.st_mode))
     continue;
   /// etc...
 } 

您可能需要处理许多情况。另见errno(3)。自我的符号链接呢? unix(7) 套接字呢? fifo(7)-s?权限?

(我们没有时间和空间来解释所有细节;您确实需要大量阅读)

【讨论】:

  • 请不要只将人们指向一堆联机帮助页,联机帮助页通常只有在您已经内化如何阅读它们时才有意义。另外,请推荐fts 而不是nftw,因为nftw 不保证是线程安全的,也可能不支持非常大的目录。
  • @zwol: nftw() 绝对支持非常大的目录。众所周知,GNU C 库实现是线程安全的,除非其中一个函数(或另一个线程)更改了当前工作目录(nftw() 本身不会更改它)。 POSIX.1 不保证。 nftw() 不需要每个深度的描述符;它每个深度只使用最多一个。另外,它是 POSIX.1-2008,而 fts 是特定于 BSD 的(但恰好受 GNU C 库支持)。
【解决方案2】:

通常情况下,如果你有

d = opendir(dirname);
while((dir = readdir(d)) != NULL) {
    some_operation(dir->d_name);
}

some_operation 对于每个 d_name 都会失败,因为您应该传递给some_operation 的路径是${dirname}/${dir->d_name},而不仅仅是dir->d_name

不过,您的程序是硬连线的,可以将特殊目录 . 传递给 opendir;当你这样做时,将dir->d_name 传递给some_operation 是安全的,因为. 始终是当前工作目录。相反,您的问题是 readlink 在应用于损坏的符号链接时不会失败,但 在应用于不是符号链接的目录条目时会失败。如果您在错误消息中包含dir->d_namestrerror(errno),您自己会更容易解决这个问题,如下所示:

d = opendir(".");
while ((dir = readdir(d)) != 0) {
   char buff[256];
   if (readlink(dir->d_name, buff, sizeof buff) {
       printf("%s: readlink failed: %s\n", dir->d_name, strerror(errno));
   } else {
       printf("%s -> %s\n", dir->d_name, buff);
   }
}

如果你这样做了,你会得到这样的输出:

.gnome2: readlink failed: Invalid argument
.python_history: readlink failed: Invalid argument
test.s: readlink failed: Invalid argument
bin -> .local/bin
[etc]

然后您可能会想到查看 readlink 联机帮助页并发现它在应用于非符号链接时返回 EINVAL

检测损坏的符号链接的正确方法是观察lstat 成功但stat 失败并显示ENOENT

struct stat lst, st;
if (lstat(dir->d_name, &lst)) {
  /* some other kind of problem */
} else if (!S_ISLNK(lst.st_mode)) {
  /* not a symlink at all */
} else if (stat(dir->d_name, &st)) {
  if (errno == ENOENT) {
    /* broken symlink */
  } else {
    /* some other kind of problem */
  }
} else {
  /* valid symlink, `lst` tells you about the link,
     `st` tells you about what it points to */
}

如果您不需要来自lst 的任何其他信息,并且您的文件系统支持d_type,则可以跳过lstat 调用:

if (dir->d_type == DT_LNK) {
   struct stat st;
   if (stat(dir->d_name, &st)) {
     if (errno == ENOENT) {
       /* broken symlink */
     }
   }
 }

但是不要忽视在DT_UNKNOWN 的情况下同时使用lstatstat,否则当您尝试在不报告的文件系统上运行程序时会感到难过d_type 信息。

【讨论】:

    猜你喜欢
    • 2011-02-17
    • 1970-01-01
    • 1970-01-01
    • 2013-06-16
    • 2013-12-05
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多