chdir 系统调用及示例

chdir – 改变当前工作目录

函数介绍

chdir系统调用用于改变进程的当前工作目录。成功调用后,进程的所有相对路径操作都基于新的工作目录进行。

函数原型

#include <unistd.h>

int chdir(const char *path);

功能

改变进程当前工作目录到指定路径。

参数

  • const char *path: 目标目录的路径名(可以是相对路径或绝对路径)

返回值

  • 成功时返回0
  • 失败时返回-1,并设置errno:
    • EACCES: 权限不足
    • EIO: I/O错误
    • ELOOP: 符号链接循环
    • ENAMETOOLONG: 路径名过长
    • ENOENT: 目录不存在
    • ENOTDIR: 路径不是目录
    • EROFS: 目录在只读文件系统上

相似函数

  • fchdir(): 通过文件描述符改变当前工作目录
  • getcwd(): 获取当前工作目录

示例代码

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>

int main() {
    char buffer[PATH_MAX];
    char original_dir[PATH_MAX];
    
    printf("=== Chdir函数示例 ===\n");
    
    // 保存原始目录
    if (getcwd(original_dir, sizeof(original_dir)) == NULL) {
        perror("获取原始目录失败");
        exit(EXIT_FAILURE);
    }
    printf("原始工作目录: %s\n", original_dir);
    
    // 示例1: 基本的目录切换操作
    printf("\n示例1: 基本的目录切换操作\n");
    
    // 切换到根目录
    if (chdir("/") == -1) {
        perror("切换到根目录失败");
    } else {
        printf("成功切换到根目录\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 切换回原始目录
    if (chdir(original_dir) == -1) {
        perror("返回原始目录失败");
    } else {
        printf("成功返回原始目录\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 示例2: 创建测试环境
    printf("\n示例2: 创建测试环境\n");
    
    // 创建测试目录结构
    const char *base_dir = "test_chdir_base";
    const char *sub_dir1 = "test_chdir_base/subdir1";
    const char *sub_dir2 = "test_chdir_base/subdir2";
    const char *deep_dir = "test_chdir_base/subdir1/deepdir";
    
    // 创建目录
    if (mkdir(base_dir, 0755) == -1 && errno != EEXIST) {
        perror("创建基础目录失败");
    } else {
        printf("创建基础目录: %s\n", base_dir);
    }
    
    if (mkdir(sub_dir1, 0755) == -1 && errno != EEXIST) {
        perror("创建子目录1失败");
    } else {
        printf("创建子目录1: %s\n", sub_dir1);
    }
    
    if (mkdir(sub_dir2, 0755) == -1 && errno != EEXIST) {
        perror("创建子目录2失败");
    } else {
        printf("创建子目录2: %s\n", sub_dir2);
    }
    
    if (mkdir(deep_dir, 0755) == -1 && errno != EEXIST) {
        perror("创建深层目录失败");
    } else {
        printf("创建深层目录: %s\n", deep_dir);
    }
    
    // 在目录中创建测试文件
    int fd = open("test_chdir_base/test_file.txt", O_CREAT | O_WRONLY, 0644);
    if (fd != -1) {
        const char *content = "Test file content for chdir demonstration";
        write(fd, content, strlen(content));
        close(fd);
        printf("创建测试文件: test_chdir_base/test_file.txt\n");
    }
    
    // 示例3: 绝对路径和相对路径切换
    printf("\n示例3: 绝对路径和相对路径切换\n");
    
    // 使用绝对路径切换
    char absolute_path[PATH_MAX * 2];
    snprintf(absolute_path, sizeof(absolute_path), "%s/%s", original_dir, base_dir);
    printf("使用绝对路径切换: %s\n", absolute_path);
    
    if (chdir(absolute_path) == -1) {
        perror("使用绝对路径切换失败");
    } else {
        printf("绝对路径切换成功\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 使用相对路径切换到子目录
    printf("使用相对路径切换到子目录1\n");
    if (chdir("subdir1") == -1) {
        perror("切换到子目录1失败");
    } else {
        printf("切换到子目录1成功\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 使用相对路径返回上级目录
    printf("使用相对路径返回上级目录\n");
    if (chdir("..") == -1) {
        perror("返回上级目录失败");
    } else {
        printf("返回上级目录成功\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 切换到深层目录
    printf("切换到深层目录\n");
    if (chdir("subdir1/deepdir") == -1) {
        perror("切换到深层目录失败");
    } else {
        printf("切换到深层目录成功\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 使用多个..返回
    printf("使用多个..返回原始目录\n");
    if (chdir("../../..") == -1) {
        perror("多级返回失败");
    } else {
        printf("多级返回成功\n");
        if (getcwd(buffer, sizeof(buffer)) != NULL) {
            printf("当前目录: %s\n", buffer);
        }
    }
    
    // 示例4: 目录切换的副作用演示
    printf("\n示例4: 目录切换的副作用演示\n");
    
    // 切换到测试目录
    if (chdir(base_dir) == -1) {
        perror("切换到测试目录失败");
    } else {
        printf("切换到测试目录\n");
        
        // 在当前目录创建文件
        fd = open("created_in_cwd.txt", O_CREAT | O_WRONLY, 0644);
        if (fd != -1) {
            const char *file_content = "File created in current working directory";
            write(fd, file_content, strlen(file_content));
            close(fd);
            printf("在当前目录创建文件: created_in_cwd.txt\n");
        }
        
        // 列出当前目录文件
        printf("当前目录文件:\n");
        system("ls -la");
        
        // 切换目录后再次查看
        if (chdir("subdir1") == -1) {
            perror("切换到子目录失败");
        } else {
            printf("\n切换到子目录后:\n");
            system("ls -la");
        }
    }
    
    // 返回原始目录
    if (chdir(original_dir) == -1) {
        perror("返回原始目录失败");
    }
    
    // 示例5: 错误处理演示
    printf("\n示例5: 错误处理演示\n");
    
    // 尝试切换到不存在的目录
    if (chdir("/nonexistent/directory") == -1) {
        printf("切换到不存在的目录: %s\n", strerror(errno));
    }
    
    // 尝试切换到文件而不是目录
    if (chdir("/etc/passwd") == -1) {
        printf("切换到文件而非目录: %s\n", strerror(errno));
    }
    
    // 尝试切换到没有权限的目录
    if (chdir("/root") == -1) {
        printf("切换到无权限目录: %s\n", strerror(errno));
    }
    
    // 尝试使用过长的路径名
    char long_path[PATH_MAX + 100];
    memset(long_path, 'a', sizeof(long_path) - 1);
    long_path[sizeof(long_path) - 1] = '\0';
    if (chdir(long_path) == -1) {
        printf("使用过长路径名: %s\n", strerror(errno));
    }
    
    // 示例6: 实际应用场景
    printf("\n示例6: 实际应用场景\n");
    
    // 场景1: 程序初始化时切换到工作目录
    printf("场景1: 程序工作目录设置\n");
    const char *work_dir = "/tmp";
    if (chdir(work_dir) == -1) {
        printf("无法切换到工作目录 %s: %s\n", work_dir, strerror(errno));
        printf("使用当前目录作为工作目录\n");
    } else {
        printf("成功切换到工作目录: %s\n", work_dir);
    }
    
    // 场景2: 备份脚本中的目录操作
    printf("场景2: 备份操作模拟\n");
    char backup_dir[PATH_MAX];
    snprintf(backup_dir, sizeof(backup_dir), "%s/backup_test", original_dir);
    
    // 创建备份目录
    if (mkdir(backup_dir, 0755) == -1 && errno != EEXIST) {
        perror("创建备份目录失败");
    } else {
        printf("创建备份目录: %s\n", backup_dir);
        
        // 切换到备份目录
        if (chdir(backup_dir) == -1) {
            perror("切换到备份目录失败");
        } else {
            printf("切换到备份目录进行操作\n");
            
            // 模拟备份操作
            system("echo 'Backup operation in progress...' > backup.log");
            system("date >> backup.log");
            printf("备份操作记录已保存\n");
        }
    }
    
    // 场景3: 构建系统中的目录管理
    printf("场景3: 构建系统目录管理\n");
    struct {
        const char *dir_name;
        const char *purpose;
    } build_dirs[] = {
        {"src", "源代码目录"},
        {"include", "头文件目录"},
        {"lib", "库文件目录"},
        {"bin", "可执行文件目录"},
        {"obj", "目标文件目录"}
    };
    
    // 切换到基础目录
    if (chdir(base_dir) == -1) {
        perror("切换到基础目录失败");
    } else {
        printf("在 %s 中创建构建目录结构:\n", base_dir);
        
        for (int i = 0; i < 5; i++) {
            if (mkdir(build_dirs[i].dir_name, 0755) == -1 && errno != EEXIST) {
                printf("  创建 %s 失败: %s\n", build_dirs[i].dir_name, strerror(errno));
            } else {
                printf("  创建 %s (%s)\n", build_dirs[i].dir_name, build_dirs[i].purpose);
            }
        }
    }
    
    // 场景4: Web服务器目录切换
    printf("场景4: Web服务器目录安全\n");
    const char *web_root = "/var/www";
    printf("Web服务器尝试切换到根目录: %s\n", web_root);
    
    // 检查目录是否存在和可访问
    if (access(web_root, F_OK) == 0) {
        if (access(web_root, R_OK | X_OK) == 0) {
            printf("目录存在且可访问\n");
            // 在实际应用中会进行chdir操作
        } else {
            printf("目录存在但权限不足\n");
        }
    } else {
        printf("目录不存在或无法访问\n");
    }
    
    // 示例7: 目录切换的安全考虑
    printf("\n示例7: 目录切换的安全考虑\n");
    
    // 保存原始目录文件描述符(用于安全返回)
    int original_fd = open(".", O_RDONLY);
    if (original_fd != -1) {
        printf("保存原始目录文件描述符: %d\n", original_fd);
        
        // 执行目录切换
        if (chdir(base_dir) == -1) {
            perror("切换目录失败");
        } else {
            printf("切换到测试目录\n");
            
            // 执行一些操作
            system("pwd");
            
            // 使用文件描述符安全返回
            if (fchdir(original_fd) == -1) {
                perror("使用文件描述符返回失败");
            } else {
                printf("使用文件描述符安全返回原始目录\n");
                system("pwd");
            }
        }
        
        close(original_fd);
    }
    
    // 示例8: 相对路径解析演示
    printf("\n示例8: 相对路径解析\n");
    
    if (chdir(base_dir) == -1) {
        perror("切换到测试目录失败");
    } else {
        printf("当前目录: ");
        system("pwd");
        
        // 相对路径解析示例
        struct {
            const char *relative_path;
            const char *description;
        } paths[] = {
            {".", "当前目录"},
            {"..", "上级目录"},
            {"./subdir1", "当前目录下的子目录"},
            {"../subdir2", "上级目录下的另一个子目录"},
            {"subdir1/./deepdir", "带.的路径"},
            {"subdir1/../subdir2", "带..的路径"}
        };
        
        for (int i = 0; i < 6; i++) {
            printf("路径 '%s' (%s):\n", paths[i].relative_path, paths[i].description);
            
            // 保存当前位置
            int save_fd = open(".", O_RDONLY);
            if (save_fd != -1) {
                // 尝试切换
                if (chdir(paths[i].relative_path) == 0) {
                    printf("  切换成功: ");
                    system("pwd");
                    
                    // 返回原位置
                    if (fchdir(save_fd) == -1) {
                        perror("  返回失败");
                    }
                } else {
                    printf("  切换失败: %s\n", strerror(errno));
                }
                close(save_fd);
            }
            printf("\n");
        }
    }
    
    // 返回原始目录
    if (chdir(original_dir) == -1) {
        perror("最终返回原始目录失败");
    }
    
    // 清理测试资源
    printf("\n清理测试资源...\n");
    
    // 删除测试文件
    char test_file_path[PATH_MAX * 2];
    snprintf(test_file_path, sizeof(test_file_path), "%s/%s/created_in_cwd.txt", 
             original_dir, base_dir);
    if (access(test_file_path, F_OK) == 0) {
        unlink(test_file_path);
        printf("删除测试文件\n");
    }
    
    // 删除备份目录和文件
    char backup_file_path[PATH_MAX * 2];
    snprintf(backup_file_path, sizeof(backup_file_path), "%s/backup.log", backup_dir);
    if (access(backup_file_path, F_OK) == 0) {
        unlink(backup_file_path);
    }
    if (access(backup_dir, F_OK) == 0) {
        rmdir(backup_dir);
        printf("删除备份目录\n");
    }
    
    // 删除构建目录
    if (chdir(base_dir) == 0) {
        for (int i = 0; i < 5; i++) {
            rmdir(build_dirs[i].dir_name);
        }
        chdir(original_dir);
    }
    
    // 删除测试目录结构
    char deep_dir_path[PATH_MAX * 2];
    snprintf(deep_dir_path, sizeof(deep_dir_path), "%s/%s", original_dir, deep_dir);
    if (access(deep_dir_path, F_OK) == 0) {
        rmdir(deep_dir_path);
    }
    
    char subdir1_path[PATH_MAX * 2];
    snprintf(subdir1_path, sizeof(subdir1_path), "%s/%s", original_dir, sub_dir1);
    if (access(subdir1_path, F_OK) == 0) {
        rmdir(subdir1_path);
    }
    
    char subdir2_path[PATH_MAX * 2];
    snprintf(subdir2_path, sizeof(subdir2_path), "%s/%s", original_dir, sub_dir2);
    if (access(subdir2_path, F_OK) == 0) {
        rmdir(subdir2_path);
    }
    
    char test_file[PATH_MAX * 2];
    snprintf(test_file, sizeof(test_file), "%s/%s/test_file.txt", original_dir, base_dir);
    if (access(test_file, F_OK) == 0) {
        unlink(test_file);
    }
    
    char base_dir_path[PATH_MAX * 2];
    snprintf(base_dir_path, sizeof(base_dir_path), "%s/%s", original_dir, base_dir);
    if (access(base_dir_path, F_OK) == 0) {
        rmdir(base_dir_path);
        printf("删除测试目录结构完成\n");
    }
    
    return 0;
}
此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注