在 Linux 系统编程中,opendir()
、readdir()
和 closedir()
是用于目录操作的三个关键函数。它们通常与 struct dirent
结构体一起使用,用于遍历目录中的文件和子目录。
1. opendir()
函数原型:
#include <dirent.h>
DIR *opendir(const char *name);
功能:
- 打开指定路径
name
的目录,并返回一个指向DIR
类型的指针,该指针用于后续的目录遍历操作。 - 如果成功打开目录,则返回指向
DIR
结构体的指针;如果失败,则返回NULL
。
参数:
name
:要打开的目录的路径名。
2. readdir()
函数原型:
#include <dirent.h>
struct dirent *readdir(DIR *dir);
功能:
- 从先前使用
opendir()
打开的目录流dir
中读取下一个目录项。 - 返回一个指向
struct dirent
结构体的指针,指向读取的目录项。 - 返回
NULL
表示已经读取到目录流的末尾或者出现错误。
参数:
dir
:通过opendir()
返回的DIR*
类型指针。
3. closedir()
函数原型:
#include <dirent.h>
int closedir(DIR *dir);
功能:
- 关闭先前使用
opendir()
打开的目录流dir
。 - 成功关闭返回
0
,失败返回-1
。
参数:
dir
:通过opendir()
返回的DIR*
类型指针。
示例代码:
#include <stdio.h>
#include <dirent.h>
int main() {
DIR *dir;
struct dirent *entry;
// 打开当前目录 "."
dir = opendir(".");
if (dir == NULL) {
perror("opendir");
return 1;
}
// 读取并打印目录中的文件和子目录
while ((entry = readdir(dir)) != NULL) {
printf("%s\n", entry->d_name);
}
// 关闭目录流
closedir(dir);
return 0;
}
代码注释解析:
opendir(".")
打开当前目录 ".",返回一个DIR*
类型的指针dir
。- 使用
readdir(dir)
逐个读取目录中的条目,readdir()
返回一个指向struct dirent
结构体的指针entry
。 entry->d_name
是struct dirent
结构体中保存目录项名字的字段。closedir(dir)
关闭目录流。
struct dirent 结构体
在 <dirent.h>
头文件中定义了 struct dirent
结构体,是用于存储目录项信息的结构体,在 Linux 系统编程中经常与 opendir()
、readdir()
、closedir()
等函数一起使用,用于遍历目录中的文件和子目录。
struct dirent {
ino_t d_ino; /* inode 号码 */
off_t d_off; /* 下一个目录项的偏移量 */
unsigned short d_reclen; /* 目录项的长度 */
unsigned char d_type; /* 文件类型;不是所有文件系统都支持该字段 */
char d_name[]; /* 文件名 */
};
-
d_ino:
- 类型:
ino_t
- 功能:目录项的 inode 号码,每个文件和目录在文件系统中有唯一的 inode 号码。
- 类型:
-
d_off:
- 类型:
off_t
- 功能:目录项在目录流中的偏移量,在某些文件系统中可能无效。
- 类型:
-
d_reclen:
- 类型:
unsigned short
- 功能:目录项的长度。
- 类型:
-
d_type:
- 类型:
unsigned char
- 功能:文件类型,不是所有的文件系统都支持这个字段。
DT_UNKNOWN
:未知类型DT_REG
:普通文件DT_DIR
:目录DT_FIFO
:命名管道(FIFO)DT_SOCK
:套接字(socket)DT_CHR
:字符设备DT_BLK
:块设备DT_LNK
:符号链接DT_WHT
:空白文件
- 类型:
-
d_name:
- 类型:
char[]
- 功能:文件或目录的名称,由于其长度是动态的,因此在结构体定义中没有具体的长度限制。
- 类型:
4. mkdir()
系统调用原型:
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
功能:
- 创建一个新目录。
- 如果创建成功,返回
0
;失败返回-1
,并设置errno
来指示错误类型。
参数:
pathname
:要创建的目录路径。mode
:新目录的权限模式。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int main() {
const char *dir_path = "mydir";
// 创建新目录
if (mkdir(dir_path, 0777) == 0) {
printf("目录 \"%s\" 创建成功\n", dir_path);
} else {
perror("mkdir");
return 1;
}
return 0;
}
代码注释解释:
mkdir(dir_path, 0777)
尝试创建名为"mydir"
的新目录,权限为0777
(即 rwxrwxrwx)。- 如果成功,打印成功信息;否则,使用
perror()
打印错误信息。
5. rmdir()
系统调用原型:
#include <unistd.h>
int rmdir(const char *pathname);
功能:
- 删除一个空目录。
- 如果删除成功,返回
0
;失败返回-1
,并设置errno
来指示错误类型。
参数:
pathname
:要删除的目录路径。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main() {
const char *dir_path = "mydir";
// 删除目录
if (rmdir(dir_path) == 0) {
printf("目录 \"%s\" 删除成功\n", dir_path);
} else {
perror("rmdir");
return 1;
}
return 0;
}
6. chdir()
系统调用原型:
#include <unistd.h>
int chdir(const char *path);
功能:
- 改变当前工作目录到指定路径
path
。 - 如果成功,返回
0
;失败返回-1
,并设置errno
来指示错误类型。
参数:
path
:要改变到的目标目录路径。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main() {
const char *new_dir = "/tmp";
// 改变当前工作目录到 /tmp
if (chdir(new_dir) == 0) {
printf("当前工作目录已改变到:%s\n", new_dir);
} else {
perror("chdir");
return 1;
}
return 0;
}
7. getcwd()
系统调用原型:
#include <unistd.h>
char *getcwd(char *buf, size_t size);
功能:
- 获取当前工作目录的路径名,并存储在
buf
指向的缓冲区中。 - 如果成功,返回指向
buf
的指针;失败返回NULL
,并设置errno
来指示错误类型。
参数:
buf
:指向用于存储路径名的缓冲区。size
:缓冲区的大小。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main() {
char buf[1024];
// 获取当前工作目录
if (getcwd(buf, sizeof(buf)) != NULL) {
printf("当前工作目录:%s\n", buf);
} else {
perror("getcwd");
return 1;
}
return 0;
}
1. 递归复制目录和文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
void copyFile(const char *src, const char *dst);
void copyDir(const char *src, const char *dst);
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <source_dir> <destination_dir>\n", argv[0]);
return 1;
}
copyDir(argv[1], argv[2]);
return 0;
}
void copyFile(const char *src, const char *dst) {
FILE *source = fopen(src, "r");
FILE *destination = fopen(dst, "w");
if (source == NULL || destination == NULL) {
perror("File open error");
return;
}
char buffer[1024];
size_t bytesRead;
while ((bytesRead = fread(buffer, 1, sizeof(buffer), source)) > 0) {
fwrite(buffer, 1, bytesRead, destination);
}
fclose(source);
fclose(destination);
}
void copyDir(const char *src, const char *dst) {
DIR *dir;
struct dirent *entry;
struct stat statbuf;
char srcPath[1024], dstPath[1024];
// 创建目标目录
mkdir(dst, 0777);
// 打开源目录
if ((dir = opendir(src)) == NULL) {
perror("opendir");
return;
}
// 逐项读取源目录内容
while ((entry = readdir(dir)) != NULL) {
// 构造源文件或目录的完整路径
snprintf(srcPath, sizeof(srcPath), "%s/%s", src, entry->d_name);
snprintf(dstPath, sizeof(dstPath), "%s/%s", dst, entry->d_name);
// 获取文件或目录信息
if (lstat(srcPath, &statbuf) < 0) {
perror("lstat");
closedir(dir);
return;
}
// 如果是目录,则递归复制目录
if (S_ISDIR(statbuf.st_mode)) {
// 忽略 "." 和 ".." 目录
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
copyDir(srcPath, dstPath);
} else {
// 如果是文件,则复制文件
copyFile(srcPath, dstPath);
}
}
closedir(dir);
}
注释解释:
copyFile()
函数用于复制文件,将源文件src
复制到目标文件dst
。copyDir()
函数用于递归复制目录和其中的文件。它遍历源目录src
中的每个文件和子目录,并根据类型分别进行复制或递归调用。main()
函数接受两个命令行参数作为源目录和目标目录,并调用copyDir()
开始复制。
2. 递归删除目录和文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
void deleteFile(const char *path);
void deleteDir(const char *path);
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
return 1;
}
deleteDir(argv[1]);
return 0;
}
void deleteFile(const char *path) {
if (unlink(path) == -1) {
perror("unlink");
}
}
void deleteDir(const char *path) {
DIR *dir;
struct dirent *entry;
struct stat statbuf;
char fullPath[1024];
// 打开目录
if ((dir = opendir(path)) == NULL) {
perror("opendir");
return;
}
// 逐项读取目录内容
while ((entry = readdir(dir)) != NULL) {
// 构造文件或目录的完整路径
snprintf(fullPath, sizeof(fullPath), "%s/%s", path, entry->d_name);
// 获取文件或目录信息
if (lstat(fullPath, &statbuf) < 0) {
perror("lstat");
closedir(dir);
return;
}
// 如果是目录,则递归删除目录
if (S_ISDIR(statbuf.st_mode)) {
// 忽略 "." 和 ".." 目录
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
deleteDir(fullPath);
} else {
// 如果是文件,则删除文件
deleteFile(fullPath);
}
}
closedir(dir);
// 删除空目录本身
if (rmdir(path) == -1) {
perror("rmdir");
}
}
注释解释:
deleteFile()
函数用于删除文件,通过unlink()
系统调用删除指定路径的文件。deleteDir()
函数用于递归删除目录及其中的所有文件和子目录。它遍历目录中的每个文件和子目录,并根据类型分别进行删除或递归调用。main()
函数接受一个命令行参数作为要删除的目录,并调用deleteDir()
开始删除。
3. 递归打印目录和文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
void printDir(const char *path, int depth);
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
return 1;
}
printf("目录结构打印:\n");
printDir(argv[1], 0);
return 0;
}
void printDir(const char *path, int depth) {
DIR *dir;
struct dirent *entry;
struct stat statbuf;
char fullPath[1024];
// 打开目录
if ((dir = opendir(path)) == NULL) {
perror("opendir");
return;
}
// 逐项读取目录内容
while ((entry = readdir(dir)) != NULL) {
// 构造文件或目录的完整路径
snprintf(fullPath, sizeof(fullPath), "%s/%s", path, entry->d_name);
// 获取文件或目录信息
if (lstat(fullPath, &statbuf) < 0) {
perror("lstat");
closedir(dir);
return;
}
// 打印文件或目录名,根据深度缩进显示
printf("%*s%s\n", depth * 2, "", entry->d_name);
// 如果是目录,则递归打印目录
if (S_ISDIR(statbuf.st_mode)) {
// 忽略 "." 和 ".." 目录
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
printDir(fullPath, depth + 1);
}
}
closedir(dir);
}
注释解释:
printDir()
函数用于递归打印目录结构及其中的文件和子目录。它遍历目录中的每个文件和子目录,并根据深度进行适当的缩进显示。main()
函数接受一个命令行参数作为要打印的目录,并调用printDir()
开始打印目录结构。
struct stat 结构体详解
在 <sys/stat.h>
头文件中定义了 struct stat
结构体,struct stat
结构体在 Unix/Linux 系统编程中非常重要,用于获取文件或目录的详细属性信息。它通常与系统调用 stat()
、lstat()
、fstat()
等配合使用,用于获取文件的元数据(metadata),包括文件类型、权限、大小、拥有者等信息。其定义如下:
struct stat {
dev_t st_dev; /* 文件的设备 ID */
ino_t st_ino; /* 文件的 inode 号 */
mode_t st_mode; /* 文件的类型和权限 */
nlink_t st_nlink; /* 文件的硬链接计数 */
uid_t st_uid; /* 文件的所有者 ID */
gid_t st_gid; /* 文件的组 ID */
dev_t st_rdev; /* 如果是设备文件,设备 ID */
off_t st_size; /* 文件的总字节数 */
blksize_t st_blksize; /* 文件系统 I/O 缓冲区大小 */
blkcnt_t st_blocks; /* 文件分配的块数 */
struct timespec st_atim; /* 文件的最后访问时间 */
struct timespec st_mtim; /* 文件的最后修改时间 */
struct timespec st_ctim; /* 文件的最后状态变化时间 */
};
struct stat 成员详解:
-
st_dev:
- 类型:
dev_t
- 功能:文件所在设备的设备 ID。
- 类型:
-
st_ino:
- 类型:
ino_t
- 功能:文件的 inode 号,唯一标识文件。
- 类型:
-
st_mode:
- 类型:
mode_t
- 功能:文件的类型和权限。可以通过宏(如
S_ISREG()
、S_ISDIR()
等)来判断文件类型及其权限。
- 类型:
-
st_nlink:
- 类型:
nlink_t
- 功能:文件的硬链接数目。
- 类型:
-
st_uid:
- 类型:
uid_t
- 功能:文件所有者的用户 ID。
- 类型:
-
st_gid:
- 类型:
gid_t
- 功能:文件所属组的组 ID。
- 类型:
-
st_rdev:
- 类型:
dev_t
- 功能:如果文件是设备文件,则为设备的设备 ID。
- 类型:
-
st_size:
- 类型:
off_t
- 功能:文件的总字节数。
- 类型:
-
st_blksize:
- 类型:
blksize_t
- 功能:文件系统 I/O 缓冲区大小。
- 类型:
-
st_blocks:
- 类型:
blkcnt_t
- 功能:文件分配的块数(每块大小由
st_blksize
决定)。
- 类型:
-
st_atim:
- 类型:
struct timespec
- 功能:文件的最后访问时间。
- 类型:
-
st_mtim:
- 类型:
struct timespec
- 功能:文件的最后修改时间。
- 类型:
-
st_ctim:
- 类型:
struct timespec
- 功能:文件的最后状态变化时间(例如权限更改)。
- 类型:
使用说明:
struct stat
结构体的成员可以通过调用stat()
、lstat()
、fstat()
等系统调用来填充,从而获取文件的详细信息。- 在使用
stat()
等系统调用时,应检查返回值以判断操作是否成功,并处理可能的错误。 st_mode
成员可以通过使用一系列宏(如S_ISREG()
、S_ISDIR()
)来检查文件的类型,例如普通文件、目录等。- 时间信息(
st_atim
、st_mtim
、st_ctim
)通常以struct timespec
结构体表示,包含了秒数和纳秒数。