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;
}