文件和目录之函数mkdir、mkdirat、rmdir和读目录函数
程序员文章站
2022-05-13 19:08:41
...
用mkdir
和mkdirat
函数来创建目录,用rmdir
函数删除目录。
#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
两个函数返回值:若成功,返回 0;若失败,返回 -1
mkdir
和mkdirat
函数在fd
指定为AT_FDCWD
或pathname
参数指定为绝对路径名时两者是完全一样的。否则,fd
参数是一个打开目录,相对路径名根据此打开目录进行计算。
测试示例
void test1(){
if(mkdir("mkdirfile", 0775) < 0)
err_sys("mkdir error.");
printf("mkdir success.\n");
}
void test2(){
if(mkdirat(AT_FDCWD, "mkdiratfile", 0775) < 0)
err_sys("mkdirat error.");
int fd;
printf("mkdirat success.\n");
if((fd=open("../exercise", O_RDONLY)) < 0)
err_sys("open error.");
if(mkdirat(fd, "mkdiratfile2", 0755) < 0)
err_sys("mkdirat error.");
printf("mkdirat success.\n");
close(fd);
}
结果如下:
通过上图可知,在当前目录下mkdir
创建了mkdirfile目录,当mkdirat
函数fd
参数指定为AT_FDCWD
时,与mkdir
函数完全一样,mkdiratfile则是在当前目录下由mkdirat
函数创建的目录;当fd
参数指定为../exercise
(即为打开一个目录)时,则相对路径名依此进行计算并在创建了../exercise
下创建了mkdiratfile2。
用rmdir
函数可以删除一个空目录,空目录即为只包含.
和..
的目录。
#include <unistd.h>
int rmdir(const char *pathname);
函数返回值: 若成功,返回 0; 若出错,返回 -1。
测试示例:
void test3(){
if(rmdir("mkdirfile") < 0)
err_sys("rmdir error.");
printf("rmdir success.\n");
}
结果如下:
由图可知mkdirfile目录被删除。
对某个目录具有访问权限的任一用户都可以读该目录,但是为了防止文件系统产生混乱,只有内核才能写目录。
#include <dirent.h>
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
两个函数返回值:若成功,返回 0;若出错,返回NULL。
struct dirent *readdir(DIR *dp);
返回值:若成功,返回 0;若在目录尾或出错,返回NULL。
void rewinddir(DIR *dp);
int closedir(DIR *dp);
返回值:若成功,返回 0;若出错,返回 -1。
long telldir(DIR *dp);
返回值:与dp关联的目录中的当前位置
void seekdir(DIR *dp, long loc);
dirent
结构体定义在头文件<dirent.h>中,具体结构与实现有关。实现对此结构所做的定义至少包含以下两个成员:
ino_t d_ino; // i-node number
char d_name[256]; // null-terminated filename
该结构体在glibc中的具体实现如下:
struct dirent{
ino_t d_ino; // inode number
off_t d_off; // not an offset
unsigned short d_reclen; // Length of this record
unsigned char d_type; /* type of file, not supported by all filesystem types */
char d_name[256]; // null-terminated filename
};
小试牛刀,我们来使用上述目录操作编写一个简单的统计目录下个文件个数的程序如下:
#include "../../include/apue.h"
#include <dirent.h>
#include <limits.h>
/*
* function type that is called for each filename
* */
typedef int Myfunc(const char *, const struct stat *, int);
static Myfunc myfunc;
static int myftw(char *, Myfunc *);
static int dopath(Myfunc *);
static long nreg, nblk, ndir, nchr, nfifo, nslink, nsock, ntot;
int main(int argc, char *argv[])
{
int ret;
if(argc != 2)
err_quit("usage: ftw <starting-pathname>");
ret = myftw(argv[1], myfunc);
ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
if(ntot == 0)
ntot = 1;
printf("total = %7ld\n", ntot);
printf("regular files = %7ld, %5.2f %%\n", nreg, nreg*100.0/ntot);
printf("directories = %7ld, %5.2f %%\n", ndir, ndir*100.0/ntot);
printf("block special = %7ld, %5.2f %%\n", nblk, nblk*100.0/ntot);
printf("char special = %7ld, %5.2f %%\n", nchr, nchr*100.0/ntot);
printf("FIFOs = %7ld, %5.2f %%\n", nfifo, nfifo*100.0/ntot);
printf("symbolic links = %7ld, %5.2f %%\n", nslink, nslink*100.0/ntot);
printf("sockets = %7ld, %5.2f %%\n", nsock, nsock*100.0/ntot);
return 0;
}
/*
* Descend through the hierarchy, starting at "pathname".
* The caller's func() is called for every file.
* */
#define FTW_F 1
#define FTW_D 2
#define FTW_DNR 3
#define FTW_NS 4
static char *fullpath;
static size_t pathlen;
static int myftw(char *pathname, Myfunc *func)
{
fullpath = path_alloc(&pathlen);
if(pathlen <= strlen(pathname)){
pathlen = strlen(pathname) * 2;
if((fullpath = (char *)realloc(fullpath, pathlen)) == NULL)
err_sys("realloc failed");
}
strcpy(fullpath, pathname);
return (dopath(func));
}
/*
* Descend through the hierachy, starting at "fullpath".
* If "fullpath" is anything other than a directory , we lstat() it,
* call func() , and return. For a directory , we call ourself
* recursively for each name in the directory.
* */
static int dopath(Myfunc *func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret, n;
if(lstat(fullpath, &statbuf) < 0)
return (func(fullpath, &statbuf, FTW_NS));
if(S_ISDIR(statbuf.st_mode) == 0)
return (func(fullpath, &statbuf, FTW_F));
/*
* It's a directory. First call func() for the directory,
* then process each filename in the directory.
* */
if((ret = func(fullpath, &statbuf, FTW_D)) != 0)
return (ret);
n = strlen(fullpath);
if(n + NAME_MAX + 2 > (int)pathlen){
pathlen *= 2;
n = strlen(fullpath);
if((fullpath = (char *)realloc(fullpath, pathlen)) == NULL)
err_sys("realloc failed");
}
fullpath[n++] = '/';
fullpath[n] = 0;
if((dp = opendir(fullpath)) == NULL)
return (func(fullpath, &statbuf, FTW_DNR));
while((dirp = readdir(dp)) != NULL){
if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
continue;
strcpy(&fullpath[n], dirp->d_name);
if((ret = dopath(func)) != 0)
break;
}
fullpath[n-1] = 0;
if(closedir(dp) < 0)
err_ret("can't close directory %s", fullpath);
return (ret);
}
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
switch(type){
case FTW_F:
switch(statptr->st_mode & S_IFMT){
case S_IFREG: nreg++; break;
case S_IFBLK: nblk++; break;
case S_IFCHR: nchr++; break;
case S_IFIFO: nfifo++; break;
case S_IFLNK: nslink++; break;
case S_IFSOCK: nsock++; break;
case S_IFDIR:
err_dump("for S_IFDIR for %s", pathname);
}
break;
case FTW_D:
ndir++; break;
case FTW_DNR:
err_ret("can't read directory %s", pathname);
break;
case FTW_NS:
err_ret("stat error for %s", pathname);
break;
default:
err_dump("unknown type %d for pathname %s", type, pathname);
}
return (0);
}
#ifdef PATH_MAX
static long pathmax = PATH_MAX;
#else
static long pathmax = 0;
#endif
static long posix_version = 0;
static long xsi_version = 0;
/* If PATH_MAX is indeterminate, no guarantee this is adequate*/
#define PATH_MAX_GUESS 1024
char * path_alloc(size_t *sizep)
{
char *ptr;
size_t size;
if(posix_version == 0){
posix_version = sysconf(_SC_VERSION);
}
if(xsi_version == 0){
xsi_version = sysconf(_SC_XOPEN_VERSION);
}
if(pathmax == 0){
errno = 0;
if((pathmax = pathconf("/", _PC_PATH_MAX)) < 0){
if(errno == 0)
pathmax = PATH_MAX_GUESS;
else
err_sys("pathconf error for _PC_PATH_MAX");
}else{
pathmax++;
}
}
/*
* Before POSIX.1 - 2001, we aren't guaranteed that PATH_MAX includes
* the terminating null byte. Same goes for XPG3.
* */
if((posix_version < 20011L) && (xsi_version < 4))
size = pathmax + 1;
else
size = pathmax;
if((ptr = (char *)malloc(size)) == NULL)
err_sys("malloc error for pathname");
if(sizep != NULL)
*sizep = size;
return (ptr);
}
结果如下: