Linux系统编程-目录相关操作

        在 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_namestruct 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 成员详解:
  1. st_dev

    • 类型:dev_t
    • 功能:文件所在设备的设备 ID。
  2. st_ino

    • 类型:ino_t
    • 功能:文件的 inode 号,唯一标识文件。
  3. st_mode

    • 类型:mode_t
    • 功能:文件的类型和权限。可以通过宏(如 S_ISREG()S_ISDIR() 等)来判断文件类型及其权限。
  4. st_nlink

    • 类型:nlink_t
    • 功能:文件的硬链接数目。
  5. st_uid

    • 类型:uid_t
    • 功能:文件所有者的用户 ID。
  6. st_gid

    • 类型:gid_t
    • 功能:文件所属组的组 ID。
  7. st_rdev

    • 类型:dev_t
    • 功能:如果文件是设备文件,则为设备的设备 ID。
  8. st_size

    • 类型:off_t
    • 功能:文件的总字节数。
  9. st_blksize

    • 类型:blksize_t
    • 功能:文件系统 I/O 缓冲区大小。
  10. st_blocks

    • 类型:blkcnt_t
    • 功能:文件分配的块数(每块大小由 st_blksize 决定)。
  11. st_atim

    • 类型:struct timespec
    • 功能:文件的最后访问时间。
  12. st_mtim

    • 类型:struct timespec
    • 功能:文件的最后修改时间。
  13. st_ctim

    • 类型:struct timespec
    • 功能:文件的最后状态变化时间(例如权限更改)。
使用说明:
  • struct stat 结构体的成员可以通过调用 stat()lstat()fstat() 等系统调用来填充,从而获取文件的详细信息。
  • 在使用 stat() 等系统调用时,应检查返回值以判断操作是否成功,并处理可能的错误。
  • st_mode 成员可以通过使用一系列宏(如 S_ISREG()S_ISDIR())来检查文件的类型,例如普通文件、目录等。
  • 时间信息(st_atimst_mtimst_ctim)通常以 struct timespec 结构体表示,包含了秒数和纳秒数。

  • 20
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值