Обратите внимание, что в POSIX 2008 представлены fstatat()
и связанные с ними функции (системные вызовы), отличающиеся суффиксом at
для знакомого имени функции. Эти функции берут один (или два в случае renameat()
) открытых файловых дескрипторов, которые ссылаются на каталог. Это означает, что другим способом кодирования этого в системе, поддерживающей fstatat()
, будет:
DIR *dp;
struct dirent *dirp;
struct stat sb;
int dfd = open(argv[1], O_RDONLY);
if (dfd == -1)
{
fprintf(stderr, "Failed to open directory %s for reading (%d: %s)\n",
argv[1], errno, strerror(errno));
return -1;
}
if ((dp = opendir(argv[1]))==NULL)
{
perror("can't open dir");
return -1;
}
while ((dirp = readdir(dp)) != NULL)
{
if (fstatat(dfd, dirp->d_name, &sb, 9) == -1) {
fprintf(stderr, "fstatat(\"%s\") failed (%d: %s)\n",
dirp->d_name, errno, strerror(errno));
}
printf("%-20s %s\n", "File name:", dirp->d_name);
}
Использование fstatat()
и связанных функций позволяет вам использовать относительные имена путей без использования chdir()
(что опасно; трудно вернуться к тому, с чего вы начали, без использования fchdir()
) или объединение имен, как показано в основном принятом ответе. Для переносимости, вероятно, все равно целесообразно использовать конкатенацию, но я смог проверить это на Mac OS X (10.10.1) и Linux (Ubuntu 14.04), используя приведенный ниже код.
Разработан в полную программу (test-fstatat.c
):
#define _XOPEN_SOURCE 700
#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)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s directory [...]\n", argv[0]);
return -1;
}
for (int i = 1; i < argc; i++)
{
DIR *dp;
struct dirent *dirp;
struct stat sb;
int dfd = open(argv[i], O_RDONLY);
if (dfd == -1)
{
fprintf(stderr, "Failed to open directory %s for reading (%d: %s)\n",
argv[i], errno, strerror(errno));
continue;
}
if (fstat(dfd, &sb) != 0 || !S_ISDIR(sb.st_mode))
{
errno = ENOTDIR;
fprintf(stderr, "%s: %d %s\n", argv[i], errno, strerror(errno));
continue;
}
if ((dp = opendir(argv[i]))==NULL)
{
perror("can't open dir");
continue;
}
printf("%-20s %s\n", "Directory:", argv[i]);
while ((dirp = readdir(dp)) != NULL)
{
if (fstatat(dfd, dirp->d_name, &sb, 0) == -1) {
fprintf(stderr, "fstatat(\"%s\") failed (%d: %s)\n",
dirp->d_name, errno, strerror(errno));
continue;
}
printf("%-20s %s\n", "File name:", dirp->d_name);
}
closedir(dp);
close(dfd);
}
return 0;
}
Пример прогона:
$ test-fstatat ~/bin/JLSS-Dist/RCS ../src/sqltools/idsmon
Directory: /Users/jleffler/bin/JLSS-Dist/RCS
File name: .
File name: ..
File name: bomrelease.pl,v
File name: chkbodlst.sh,v
File name: chkmsdnmd.sh,v
File name: chksumtool.pl,v
File name: jdcrelease.sh,v
File name: JLSS-Dist.mk,v
File name: jlss.sh,v
File name: mkbod.sh,v
File name: mkmsd.sh,v
File name: mknmd.sh,v
File name: msd.create.sh,v
File name: MSD.sh,v
File name: prodverstamp.sh,v
File name: publictimestamp.sh,v
File name: redonmd.sh,v
Directory: ../src/sqltools/idsmon
File name: .
File name: ..
File name: acsetup.sh
File name: dumpdblflt
File name: dumpdblflt.c
File name: idsmon
File name: idsmon.o
File name: idspacket
File name: idspacket.c
File name: idspacket.o
File name: idstest
File name: idstest.c
File name: idstest.o
$