在Linux系统中递归搜索所有子目录中的文件

3
我正在尝试通过所有子目录搜索给定参数的文件。我的代码问题在于,当它到达一个不是目录的文件时,它会停止并显示“打开目录错误”的错误信息。
我找不到解决方法。我尝试了另一个if(S_ISREG...),但它没有起作用。
include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>

void check_file_existence(char *dirName,char *file){
    struct stat *metadata;
    char name[1000];
    struct dirent *dirEntry;
    struct stat bufstat;

    DIR *dir;

    dir = opendir(dirName);
    if (dir==NULL)
    {
        perror("Error opening the directory\n");
        exit(1);
    }

    while ((dirEntry = readdir(dir))!=NULL){
        lstat(dirEntry->d_name,&bufstat);

        if(S_ISDIR(bufstat.st_mode)){
            if (strcmp(dirEntry->d_name,".")==0 || strcmp(dirEntry->d_name,"..")==0){
                continue;
            }
            sprintf(name,"%s/%s",dirName,dirEntry->d_name);
            printf("%s\n",name);
            check_file_existence(name,file);
        }       
    }

    closedir(dir);
}

int main(int argc,char **argv){
    if (argc!=3){
        perror("Number of arguments is wrong.\n");
        exit(1);
    }

    check_file_existence(argv[1],argv[2]);  
}

2
你有没有考虑打印出无法打开的目录名称?它是否存在? - Jongware
3
在调用readdir之后,如果你调用stat(dirEntry->d_name)通常会失败,因为d_name只是该条目的名称,而不是其路径。通常在dirName路径和d_name之间进行连接才能进行状态检查。这是一个容易犯的错误,也是一个经常被问到的问题:参见为什么在调用readdir后无法使用stat? - Steve Summit
3
请注意,您不需要进行 lstat 调用。如果您阅读 readdir 手册页面,您将会看到 dirent 结构体有一个 d_type 成员,它会告诉您该文件是否为目录。附带地,这也将解决您的问题(或者从 lstat 调用中检查错误,而您应该始终这样做)。 - Some programmer dude
2
@SteveSummit 不,这是Linux特定的,但由于问题标记为“linux”,我认为提到它应该没问题。 - Some programmer dude
2
对我之前的评论进行评论:我看到你在递归调用之前连接dirNamed_name。如果您早些时候在调用lstat之前这样做,您的原始程序可能会正常工作。(不过,当您调用l/stat时,应该检查错误,就像Some programmer dude提到的那样。) - Steve Summit
显示剩余7条评论
1个回答

1
这里是您的代码,经过简化后解决了错误,并进行了两个改进。现在它可以递归搜索目录树以查找指定文件的第一个出现。
我们使用dirEntry结构来标识文件类型,并添加strcmp()来检查指定文件。在dirEntry结构中使用d_type是最简单的方法来确定文件类型,因此往往能减少错误率。
我们检查输入是否有额外的尾随斜线。输入中多余的斜杠不会阻止任何操作,但会使输出不够清晰。
为了方便调试,我们大量使用printf(),并添加一个例程来字节转储dirEntry结构的内容,以帮助您更详细地查看它在遍历目录和文件时所发生的情况。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>

void debugbytes( char *s, unsigned short nlen, char dtype, char *nameptr ) {
  int n;
  printf( "\n%d %x ", nlen, dtype );
  switch( dtype )
    {
    case DT_BLK:
      printf( "BLK" );
      break;
    case DT_CHR:
      printf( "CHR" );
      break;
    case DT_DIR:
      printf( "DIR" );
      break;
    case DT_FIFO:
      printf( "FIFO" );
      break;
    case DT_LNK:
      printf( "LNK" );
      break;
    case DT_REG:
      printf( "REG" );
      break;
    case DT_SOCK:
      printf( "SOCK" );
      break;
    case DT_UNKNOWN:
      printf( "UNKOWN" );
      break;
    default:
      printf( "not recognized" );
    }
  printf( " %s :", nameptr );
  for (n = 0; n < nlen; n++ ) {
    printf( "%x", s[n] );
  }
  printf( "\n" );
}

void check_file_existence(char *dirName,char *file){

  DIR *dir;

  struct dirent *dirEntry;

  char name[1000];

  printf( "opening %s\n", dirName );

  dir = opendir(dirName);
  if (dir==NULL)
    {
      perror("Error opening the directory\n");
      exit(1);
    }

    while ((dirEntry = readdir(dir))!=NULL){

      debugbytes( (char *) dirEntry,  dirEntry->d_reclen, dirEntry->d_type, dirEntry->d_name );

      if ( dirEntry->d_type == DT_DIR ) {
        if (strcmp(dirEntry->d_name,".")==0 || strcmp(dirEntry->d_name,"..")==0){
        continue;
      }
      printf( "directory\n" );
      sprintf(name,"%s/%s",dirName,dirEntry->d_name);
      printf("\n%s\n",name);
      check_file_existence(name,file);
    }

    else if ( dirEntry->d_type == DT_REG ) {

      printf( "file %s/%s\n", dirName, (char *)dirEntry->d_name );

      if ( !strcmp( dirEntry->d_name, file ) ) {
        printf( "file found\n" );
        break;
      }
    }
  }

  closedir(dir);
}

int main(int argc,char **argv){
  char dirspec[256] = { 0 };
  int n;

    if (argc!=3){
        perror("Number of arguments is wrong.\n");
        exit(1);
    }

    n = strlen( argv[1] );
    while( (n > 1) && argv[1][n-1] == '/' ) n--; 

    strncpy(dirspec, argv[1], n );

    check_file_existence( dirspec, argv[2] );  
}

这是一个样本输出:

$ ./temp1 gEDA/ 1206P.fp
opening gEDA
.
.
.
32 4 DIR Footprints :3b04250000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f2004466f6f747072696e7473004
directory
.
.
.
32 8 REG 1206P.fp :ffffff8084250000ffffffd0ffffffff12ffffffeb7afffffff77052200831323036502e6670054ffffffa7ffffffce8
file gEDA/Footprints/1206P.fp
file found

$

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接