【问题标题】:stat() giving wrong informationstat() 提供错误信息
【发布时间】:2020-06-14 16:57:20
【问题描述】:

我正在使用循环打印目录中每个文件的信息,以将 ls shell 函数重新创建为 C 程序。当将来自程序的信息与来自 ls 命令的正确信息进行比较时,来自程序的结果(使用 stat())是非常错误的。

这是我所有的程序代码:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char* params[])
{
  void printTable(char filepath[], int s, int b);

    // If no modifiers, send error
    if(argc == 1) {
      printf("Error: no directory specified. \n");
      exit(1);
      return 0;
    }
    // If only a directory is provided
    if(argc ==2) {
      printTable(params[1], 0, 0);
    } // end of argc == 2

    // If there are 4 modifiers
    else if(argc == 4) {
    }
    return 0;
}

void printTable (char filepath[], int s, int b) {
  DIR *dp;
  struct dirent *dir;
  struct stat fileStats;
  if((dp = opendir(filepath)) == NULL) {
    fprintf(stderr, "Cannot open directory. \n");
    exit(1);
  }
  printf("Processing files in the directory: %s\n", filepath);
  printf("inode \t Type \t UID \t GID \t SIZE \t       Filename \t                 Modification date \n");
  while((dir = readdir(dp)) != NULL ) {
    if(dir->d_ino != 0 && fileStats.st_ino > 0 && stat(dir->d_name, &fileStats) < 0) {
      // Print the inode
      printf("%d \t ", fileStats.st_ino);
      // Print type
      if(dir->d_type == DT_BLK)
        printf("BLK \t ");
      else if(dir->d_type == DT_CHR)
        printf("CHR \t ");
      else if(dir->d_type == DT_DIR)
        printf("DIR \t ");
      else if(dir->d_type == DT_FIFO)
        printf("FIFO \t ");
      else if(dir->d_type == DT_LNK)
        printf("LNK \t ");
      else if(dir->d_type == DT_SOCK)
        printf("SOCK \t ");
      else if(dir->d_type == DT_REG)
        printf("REG \t ");
      else
        printf("UNKOWN \t ");
      // Print UID
      printf("%d \t ", fileStats.st_uid);
      // Print GID
      printf("%d \t ", fileStats.st_gid);
      // Print SIZE
      printf("%jd bytes \t ", fileStats.st_size);
      // Print file name
      printf("%25s \t ", dir->d_name);
      // Print date modified
      printf("%20s \n", ctime(&fileStats.st_mtime));
    }
  }
  struct tm *lt = localtime(&fileStats.st_mtime);
  int diff = difftime(time(NULL), mktime(lt));
  printf("%d \n", diff);
  closedir(dp);
}

以下是 shell 命令的结果(此目录与此程序无关): shell output

这是运行程序的结果:

【问题讨论】:

  • if(dir-&gt;d_ino != 0 &amp;&amp; fileStats.st_ino &gt; 0 &amp;&amp; stat(dir-&gt;d_name, &amp;fileStats) &lt; 0) ?!?你为什么把所有这些都塞进if 条件中?!?你认为这是在做什么?
  • 为什么只有出现错误才打印结果...?

标签: c directory ls stat


【解决方案1】:

stat(2) 成功返回0,但您正在测试是否stat(dir-&gt;d_name, &amp;fileStats) &lt; 0。因此,每当stat(2) fails 实际统计文件时,您的代码就会打印一些东西。此外,条件链接的方式,fileStats.st_ino &gt; 0 是一个表达式,涉及第一次迭代中未初始化的变量。

stat(2) 失败的最可行原因是您没有列出当前目录。 dirent结构中的d_name成员是目录中的文件名,不是完整路径而是相对于目录路径的路径。当您将它传递给stat(2) 时,它仅在文件位于进程的当前工作目录(cwd)中时才会起作用。如果不是,就像您的情况一样,stat(2) 调用将失败。您需要做的是通过将filepathdir-&gt;d_name 与两者之间的路径分隔符/ 连接起来构建完整路径:

char fullpath[PATH_MAX];

sprintf(fullpath, "%s/%s", filepath, dir->d_name);
stat(fullpath, &fileStats);

或者,或者,使用chdir(2) 在循环之前更改工作目录:

char olddir[PATH_MAX];

// Save current working directory
getcwd(olddir, PATH_MAX);
// Change working directory to `filepath`
chdir(filepath);

// Do your loop here

// Restore old working directory
chdir(olddir);

现在stat(2) 可以正确使用相对路径,即,只是文件名。

【讨论】:

  • 你是我的上帝。我刚刚在循环之前添加了 chdir(filepath) 并将 stat(dir->d_name, &fileStats) d_name, &fileStats) == 0 并且它完全按预期工作。谢谢!
【解决方案2】:
stat(dir->d_name, &fileStats) < 0

应该是

stat(dir->d_name, &fileStats) == 0

只有在出现错误时才打印内容。

stat 返回:

成功时返回零。出错时,返回 -1,并且 errno 为 适当设置。

【讨论】:

  • 这与 Hristo Iliev 的使用 chdir(filepath) 的建议完美结合。谢谢!
【解决方案3】:

如果您的平台对 POSIX 2008 有足够的支持,您可以使用fstatat()dirfd() 函数来获得良好的效果。这至少适用于当前版本的 BSD、macOS、Linux、AIX、HP-UX、Solaris。

此代码类似于问题中的代码,但它不会尝试复制它解码文件类型等的方式。

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++)
    {
        const char *dir = argv[i];
        DIR *dp = opendir(dir);
        if (dp == 0)
        {
            fprintf(stderr, "%s: failed to open directory %s: %d %s\n",
                    argv[0], dir, errno, strerror(errno));
            continue;
        }
        /* dirfd(): POSIX 2008 - OK on BSD, macOS, Linux, AIX, HP-UX, Solaris */
        int fd = dirfd(dp);
        struct dirent *file;
        while ((file = readdir(dp)) != 0)
        {
            struct stat sb;
            /* 0 argument could be AT_SYMLINK_NOFOLLOW */
            if (fstatat(fd, file->d_name, &sb, 0) == 0)
            {
                /* Some of the conversion specifiers may be incorrect for some systems */
                /* Inode number, size, time in particular */
                printf("%10llu  %5d  %5d  %7o  %3d  %9lld  %10ld  %10ld  %10ld  %s/%s\n",
                       sb.st_ino, sb.st_uid, sb.st_gid, sb.st_mode, sb.st_nlink, sb.st_size,
                       sb.st_mtime, sb.st_atime, sb.st_ctime, dir, file->d_name);
            }
            else
                fprintf(stderr, "%s: failed to stat() %s/%s: %d %s\n",
                    argv[0], dir, file->d_name, errno, strerror(errno));
        }
        closedir(dp);
    }

    return 0;
}

请注意,这根本不需要(因此不需要)使用chdir()。使用fstatat() 允许您指定“解释相对于给定目录的文件名”,其中目录由文件描述符标识。

给定一个合适的错误报告包(因此程序的名称将自动包含在错误中),我会将循环体打包为一个函数。但是,错误消息中的argv[0] 引用会带来不便。对于错误报告,我将使用 GitHub 上我的 SOQ(堆栈溢出问题)存储库中可用的代码作为 src/libsoq 子目录中的文件 stderr.cstderr.h

示例输出(来自 Mac - 10 位 inode 编号比大多数系统上的要大):

$ at67 bin ~/src/ule
4296808809    501     20    40755   33       1056  1577029162  1583165629  1577029162  bin/.
4296808746    501     20    40755  208       6656  1583164216  1583165629  1583164216  bin/..
4296811200    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn
4296811205    501     20   100755    1       1266  1515986057  1583164235  1582216636  bin/rfn-c
4305347192    501     20   100755    1        246  1524096284  1582224384  1582216636  bin/soqvg
4297537255    501     20   100755    1       3813  1579639563  1582830967  1582216636  bin/pipe-rot
4296811199    501     20   100755    1        233  1515695843  1582224384  1582216636  bin/sow
4298720660    501     20   100755    1        627  1517875149  1582224384  1582216636  bin/so-getchar
4296811201    501     20   100755    1        218  1515695843  1582224384  1582216636  bin/ddpr
4296811210    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-pl
4296808811    501     20   100644    1        490  1510874880  1578595253  1510874880  bin/README.md
4296811204    501     20   100755    1       2278  1515695843  1582224384  1582216636  bin/fixin
4296811203    501     20   100755    1       2835  1576997332  1582224384  1582216636  bin/so-books
4296811196    501     20   100755    1        617  1515695843  1582224388  1582216636  bin/wso
4296811197    501     20   100755    1         85  1515695843  1583165629  1582216636  bin/so
4296808810    501     20   100644    1         92  1510874880  1579561480  1510874880  bin/.gitignore
4296811193    501     20   100755    1        200  1515695843  1582224388  1582216636  bin/posixcmd
4296811206    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-h
4451766334    501     20   100755    1        507  1576997332  1582224384  1582216636  bin/so-esql
4297012073    501     20   100755    1        937  1582216633  1583164235  1582216636  bin/sscce
4296811202    501     20   100755    1        522  1515695843  1582224384  1582216636  bin/so-late
4296811209    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-sql
4297507309    501     20   100755    1        848  1526264352  1582224384  1582216636  bin/so-stderr
4296811194    501     20   100755    1        206  1515695843  1582224388  1582216636  bin/posixfun
4342190418    501     20   100755    1       1227  1541833786  1582224384  1582216636  bin/so-quotes
4298078558    501     20   100755    1        722  1515695843  1582224384  1582216636  bin/soa
4296811198    501     20   100755    1         92  1515695843  1582224384  1582216636  bin/sops
4356366344    501     20   100755    1        454  1545845134  1582644937  1582216636  bin/so-reformat-c
4296811208    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-cpp
4298720661    501     20   100755    1        700  1576997332  1582224384  1582216636  bin/so-c-reserved
4296811207    501     20   100755    1       1266  1515986057  1582656633  1582216636  bin/rfn-sh
4296811195    501     20   100755    1        255  1515695843  1582224388  1582216636  bin/posixhdr
4451855327    501     20   100755    1        780  1577029658  1582224384  1582216636  bin/so-dotarrow
4296916142    501     20    40755   15        480  1574117045  1583165629  1574117045  /Users/jonathanleffler/src/ule/.
4296713088    501     20    40755   97       3104  1575746582  1583165539  1575746582  /Users/jonathanleffler/src/ule/..
4296917945    501     20   100444    1          7  1473056744  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-nnl
4296917947    501     20   100644    1       6148  1418098863  1578608259  1510994007  /Users/jonathanleffler/src/ule/.DS_Store
4296917957    501     20   100755    1      44824  1473056806  1578608437  1513032846  /Users/jonathanleffler/src/ule/ule-v1.4
4296917948    501     20   100444    1         15  1473056679  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-nul
4296917949    501     20   100640    1         43  1418023230  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-mix
4296917951    501     20   100644    1        745  1436058130  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule.notes
4296917952    501     20   100640    1         33  1418023230  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-unx
4296917953    501     20   100640    1         33  1418023230  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-dos
4296917954    501     20    40755   11        352  1541802114  1578608839  1541802114  /Users/jonathanleffler/src/ule/RCS
4441180506    501     20   100444    1       6726  1574116649  1578608259  1574117045  /Users/jonathanleffler/src/ule/ule.c
4296917956    501     20    40755    3         96  1437532230  1578611808  1510994007  /Users/jonathanleffler/src/ule/ule.dSYM
4297282884    501     20   100755    1      60160  1513033250  1582552763  1513033250  /Users/jonathanleffler/src/ule/ule
4296917958    501     20   100640    1         30  1425237329  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-mac
$

如果您的系统没有dirfd() 但有fstatat(),您可以使用这些函数来打开和关闭目录。 (上述代码的早期版本使用了此代码的变体——但配置它很快就会变得混乱,dirfd() 使这一切都变得不必要了。)。您可以使用dir_open(dir) 代替dirfd(dp),并在closedir(dp) 之前或之后添加dir_close(fd)

#include <fcntl.h>
#include <unistd.h>

int dir_open(const char *name)
{
    return open(name, O_RDONLY | O_DIRECTORY);
}

int dir_close(int fd)
{
    return close(fd);
}

如果您的系统有O_SEARCH,请使用它来代替O_RDONLY(例如,在我进行测试的macOS Mojave 10.14.6 上,O_SEARCH 不可用)。如果您的系统没有O_DIRECTORY,请忽略该术语。这些只是使配置变得如此混乱的一些细节。

总的来说,如果你有fstatat(),你很可能也有dirfd()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    相关资源
    最近更新 更多