如果您的平台对 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.c 和 stderr.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()。