在 Linux 系统上以特定格式打开文件而不进行迭代

Opening file with specific format on Linux systems without iteration

我有一个包含数千个文件的目录,除了一个格式为 ***.txt 的文件外,所有文件都具有 ***-***.txt 的通用格式我不知道其确切名称的其中之一。

我想知道是否可以只使用 C 或 Linux 函数并且不调用 system().[=14= 而无需遍历所有目录就可以打开单个文件]

如前所述,无法避免遍历文件名。 好消息是这真的无关紧要。即使有数千个条目(wut?!)也不应该花费太多时间,所以如果您只需要一次,您就可以开始了。

C++

// Life is easy in C++

#include <filesystem>
#include <iostream>

namespace fs = std::filesystem;

fs::path find_magic_file( const fs::path directory )
{
  for (auto & entry : fs::directory_iterator( directory ))
    if (entry.path().stem().string().find( '-' ) == std::string::npos)
      return entry.path();
  return "";
}

int main( int num_directories, char ** directory_names )
{
  for (int n = 1;  n < num_directories;  n += 1)
  {
    auto filepath = find_magic_file( directory_names[n] );
    if (!filepath.empty())
      std::cout << filepath.string() << "\n";
  }
}

C

// C requires you to recur to the OS facilities directly
// This example handles Windows and Linux/POSIX systems

#ifdef _WIN32
  #define _CRT_SECURE_NO_WARNINGS
#endif

#include <iso646.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
  #include <windows.h>
  
  char * find_magic_file( const char * directory )
  {
    WIN32_FIND_DATA entry;

    char * dirname = malloc( strlen( directory ) + 5 );
    if (!dirname) return NULL;

    HANDLE h = FindFirstFile( strcat( strcpy( dirname, directory ), "/*.*" ), &entry );
    if (h == INVALID_HANDLE_VALUE) return NULL;
    
    while ((strcmp( entry.cFileName, "."  ) == 0) 
        or (strcmp( entry.cFileName, ".." ) == 0)
        or (strchr( entry.cFileName, '-' )))
      if (!FindNextFile( h, &entry )) 
        break;
      
    FindClose( h );
    free( dirname );
    if (strchr( entry.cFileName, '-' )) return NULL;
    
    char * filepath = calloc( strlen( directory ) + 1 + strlen( entry.cFileName ) + 1, 1 );
    if (filepath) strcat( strcat( strcpy( filepath, directory ), "/" ), entry.cFileName );
    return filepath;
  }
  
#else
  #include <dirent.h>
  #include <sys/types.h>

  char * find_magic_file( const char * directory )
  {
    char * filepath = NULL;
    DIR * dir = opendir( directory );
    if (dir)
    {
      struct dirent * entry;
      while ((entry = readdir( dir )))
      {
        if (!strchr( entry->d_name, '-' )
            and (strcmp( entry->d_name, "."  ) != 0)
            and (strcmp( entry->d_name, ".." ) != 0))
        {
          filepath = malloc( strlen( directory ) + 1 + strlen( entry->d_name ) + 1 );
          if (filepath) strcat( strcat( strcpy( filepath, directory ), "/" ), entry->d_name );
          break;
        }
      }
      closedir( dir );
    }
    return filepath;
  }
#endif

int main( int num_directories, char ** directory_names )
{
  for (int n = 1;  n < num_directories;  n += 1)
  {
    char * filepath = find_magic_file( directory_names[n] );
    if (filepath)
    {
      puts( filepath );
      free( filepath );
    }
  }
}

您会注意到 main() 两种语言之间唯一真正的区别是 find_magic_file() 返回的对象类型。

在 Linux 中,您可以使用 POSIX.1 scandir() 函数,具有拒绝带破折号的文件名的过滤函数。

这样,scanning/iteration 是在 C 库中完成的,以适合手头操作系统的方式完成,如果例如,不应该混淆。文件在扫描过程中被重命名。如果将无破折号文件重命名为破折号文件,并将另一个破折号文件重命名为无破折号名称,恰好在目录扫描期间,可能找不到任何一个名称。 (而且,如果它们以相反的顺序完成,则可以看到两个名字。)

这是一个示例程序,带有完整的错误检查:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

static int dashless_filter(const struct dirent *ent)
{
    /* Reject file names that begin with a dot '.' ("hidden" files) */
    if (ent->d_name[0] == '.')
        return 0;

    /* Reject file names with dash in them */
    if (strchr(ent->d_name, '-'))
        return 0;

    return 1;
}

char *dashless(const char *dirpath)
{
    struct dirent **list = NULL;
    char           *result = NULL;
    int             num, err;

    /* Return NULL if dirpath is null or empty */
    if (!dirpath || !*dirpath) {
        errno = ENOTDIR;
        return NULL;
    }

    do {
        num = scandir(dirpath, &list, dashless_filter, NULL);
        if (num == -1) {
            err = errno;  /* errno set by scandir() */
            break;
        }

        if (num < 1 || !list || !list[0] || list[0]->d_name[0] == '[=10=]') {
            err = ENOENT;  /* No matching files found */
            break;
        } else
        if (num > 1) {
            err = EMFILE;  /* More than one matching file found. */
            break;
        }

        result = strdup(list[0]->d_name);
        if (!result) {
            err = ENOMEM;
        } else {
            err = 0;
        }
    } while (0);

    /* Free all entries in the list, */
    while (num-->0)
        free(list[num]);
    /* and the list itself. */
    free(list);

    errno = err;
    return result;
}

int main(int argc, char *argv[])
{
    int  status = EXIT_SUCCESS;
    int  arg;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
        fprintf(stderr, "       %s DIRECTORY [ DIRECTORY ... ]\n", arg0);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program reports if each specified directory contains\n");
        fprintf(stderr, "a single file without a dash '-' in its name.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    for (arg = 1; arg < argc; arg++) {
        char *filepath = dashless(argv[arg]);
        if (filepath) {
            printf("%s: %s\n", argv[arg], filepath);
            free(filepath);
        } else {
            if (errno == EMFILE)
                fprintf(stderr, "%s: Multiple dashless files exist.\n", argv[arg]);
            else
                fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
            status = EXIT_FAILURE;
        }
    }

    return status;
}

请注意,它会明确检查单个文件名。

如果生成文件的应用程序或脚本也确实为一个无破折号文件创建了一个具有固定已知名称的硬 link,则更好。这样一来,人们可以始终使用固定的 symlink/hardlink 名称,并且如果无破折号文件被重命名,则不会有任何竞争 window 期间会发现 none 或两个无破折号文件.