21. rename - 重命名文件或目录 見出しへのリンク

函数介绍 見出しへのリンク

rename系统调用用于重命名文件或目录,也可以将文件或目录移动到另一个位置。如果目标文件已存在,则会被替换。

函数原型 見出しへのリンク

#include <stdio.h>
#include <sys/types.h>
int rename(const char *oldpath, const char *newpath);

功能 見出しへのリンク

将文件或目录从旧路径重命名为新路径。

参数 見出しへのリンク

  • const char *oldpath: 原文件或目录的路径名
  • const char *newpath: 新文件或目录的路径名

返回值 見出しへのリンク

  • 成功时返回0
  • 失败时返回-1,并设置errno:
    • EACCES: 权限不足
    • EBUSY: 文件正被使用
    • EDQUOT: 磁盘配额超限
    • EEXIST: 目标文件存在且不能被替换
    • EINVAL: 参数无效
    • EISDIR: newpath是目录但oldpath是文件
    • ELOOP: 符号链接循环
    • EMLINK: 链接数超限
    • ENAMETOOLONG: 路径名过长
    • ENOENT: 原文件不存在或目标目录不存在
    • ENOSPC: 磁盘空间不足
    • ENOTDIR: 路径前缀不是目录
    • ENOTEMPTY: 目标目录非空
    • EPERM: 操作不被允许
    • EROFS: 文件系统只读
    • EXDEV: 跨文件系统移动

相似函数 見出しへのリンク

  • renameat(): 相对路径版本
  • renameat2(): 增强版本,支持更多选项

示例代码 見出しへのリンク

#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("=== Rename函数示例 ===\n");
    
    // 保存原始目录
    if (getcwd(original_dir, sizeof(original_dir)) == NULL) {
        perror("获取原始目录失败");
        exit(EXIT_FAILURE);
    }
    
    // 示例1: 基本的文件重命名操作
    printf("\n示例1: 基本的文件重命名操作\n");
    
    // 创建测试文件
    const char *old_filename = "test_rename_old.txt";
    int fd = open(old_filename, O_CREAT | O_WRONLY, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        exit(EXIT_FAILURE);
    }
    
    const char *file_content = "This is test content for rename function demonstration.";
    if (write(fd, file_content, strlen(file_content)) == -1) {
        perror("写入文件内容失败");
    }
    close(fd);
    printf("创建测试文件: %s\n", old_filename);
    
    // 重命名文件
    const char *new_filename = "test_rename_new.txt";
    if (rename(old_filename, new_filename) == -1) {
        perror("重命名文件失败");
    } else {
        printf("成功将 %s 重命名为 %s\n", old_filename, new_filename);
        
        // 验证文件是否存在
        if (access(new_filename, F_OK) == 0) {
            printf("新文件存在\n");
            
            // 读取文件内容验证
            fd = open(new_filename, O_RDONLY);
            if (fd != -1) {
                char read_buffer[200];
                ssize_t bytes_read = read(fd, read_buffer, sizeof(read_buffer) - 1);
                if (bytes_read > 0) {
                    read_buffer[bytes_read] = '\0';
                    printf("文件内容: %s\n", read_buffer);
                }
                close(fd);
            }
        }
        
        if (access(old_filename, F_OK) != 0) {
            printf("原文件已不存在\n");
        }
    }
    
    // 示例2: 目录重命名操作
    printf("\n示例2: 目录重命名操作\n");
    
    // 创建测试目录
    const char *old_dirname = "test_rename_old_dir";
    const char *new_dirname = "test_rename_new_dir";
    
    if (mkdir(old_dirname, 0755) == -1 && errno != EEXIST) {
        perror("创建测试目录失败");
    } else {
        printf("创建测试目录: %s\n", old_dirname);
        
        // 在目录中创建文件
        char file_in_dir[PATH_MAX];
        snprintf(file_in_dir, sizeof(file_in_dir), "%s/content.txt", old_dirname);
        fd = open(file_in_dir, O_CREAT | O_WRONLY, 0644);
        if (fd != -1) {
            const char *dir_content = "Content in directory file";
            write(fd, dir_content, strlen(dir_content));
            close(fd);
            printf("在目录中创建文件: content.txt\n");
        }
        
        // 重命名目录
        if (rename(old_dirname, new_dirname) == -1) {
            perror("重命名目录失败");
        } else {
            printf("成功将目录 %s 重命名为 %s\n", old_dirname, new_dirname);
            
            // 验证目录和内容
            if (access(new_dirname, F_OK) == 0) {
                printf("新目录存在\n");
                
                // 检查目录中的文件
                char new_file_path[PATH_MAX];
                snprintf(new_file_path, sizeof(new_file_path), "%s/content.txt", new_dirname);
                if (access(new_file_path, F_OK) == 0) {
                    printf("目录中的文件也已移动\n");
                }
            }
            
            if (access(old_dirname, F_OK) != 0) {
                printf("原目录已不存在\n");
            }
        }
    }
    
    // 示例3: 文件移动操作
    printf("\n示例3: 文件移动操作\n");
    
    // 创建另一个目录用于移动测试
    const char *move_to_dir = "move_destination";
    if (mkdir(move_to_dir, 0755) == -1 && errno != EEXIST) {
        perror("创建目标目录失败");
    } else {
        printf("创建目标目录: %s\n", move_to_dir);
        
        // 创建要移动的文件
        const char *file_to_move = "file_to_move.txt";
        fd = open(file_to_move, O_CREAT | O_WRONLY, 0644);
        if (fd != -1) {
            const char *move_content = "This file will be moved to another directory";
            write(fd, move_content, strlen(move_content));
            close(fd);
            printf("创建要移动的文件: %s\n", file_to_move);
        }
        
        // 移动文件到另一个目录
        char destination_path[PATH_MAX];
        snprintf(destination_path, sizeof(destination_path), "%s/%s", move_to_dir, file_to_move);
        
        if (rename(file_to_move, destination_path) == -1) {
            perror("移动文件失败");
        } else {
            printf("成功将文件 %s 移动到 %s\n", file_to_move, destination_path);
            
            // 验证移动结果
            if (access(destination_path, F_OK) == 0) {
                printf("文件已在目标位置\n");
            }
            
            if (access(file_to_move, F_OK) != 0) {
                printf("原位置文件已不存在\n");
            }
        }
    }
    
    // 示例4: 替换已存在文件
    printf("\n示例4: 替换已存在文件\n");
    
    // 创建目标文件
    const char *target_file = "target_file.txt";
    fd = open(target_file, O_CREAT | O_WRONLY, 0644);
    if (fd != -1) {
        const char *target_content = "Original content that will be replaced";
        write(fd, target_content, strlen(target_content));
        close(fd);
        printf("创建目标文件: %s\n", target_file);
    }
    
    // 创建源文件
    const char *source_file = "source_file.txt";
    fd = open(source_file, O_CREAT | O_WRONLY, 0644);
    if (fd != -1) {
        const char *source_content = "New content that will replace the old content";
        write(fd, source_content, strlen(source_content));
        close(fd);
        printf("创建源文件: %s\n", source_file);
    }
    
    // 读取替换前的目标文件内容
    printf("替换前目标文件内容:\n");
    fd = open(target_file, O_RDONLY);
    if (fd != -1) {
        char content_buffer[200];
        ssize_t bytes_read = read(fd, content_buffer, sizeof(content_buffer) - 1);
        if (bytes_read > 0) {
            content_buffer[bytes_read] = '\0';
            printf("  %s\n", content_buffer);
        }
        close(fd);
    }
    
    // 使用rename替换文件
    if (rename(source_file, target_file) == -1) {
        perror("替换文件失败");
    } else {
        printf("成功使用 %s 替换 %s\n", source_file, target_file);
        
        // 验证替换结果
        printf("替换后目标文件内容:\n");
        fd = open(target_file, O_RDONLY);
        if (fd != -1) {
            char content_buffer[200];
            ssize_t bytes_read = read(fd, content_buffer, sizeof(content_buffer) - 1);
            if (bytes_read > 0) {
                content_buffer[bytes_read] = '\0';
                printf("  %s\n", content_buffer);
            }
            close(fd);
        }
        
        // 源文件应该不存在了
        if (access(source_file, F_OK) != 0) {
            printf("源文件已被移除\n");
        }
    }
    
    // 示例5: 错误处理演示
    printf("\n示例5: 错误处理演示\n");
    
    // 尝试重命名不存在的文件
    if (rename("nonexistent_file.txt", "new_name.txt") == -1) {
        printf("重命名不存在的文件: %s\n", strerror(errno));
    }
    
    // 尝试重命名到只读文件系统
    if (rename(target_file, "/proc/test") == -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 (rename(target_file, long_path) == -1) {
        printf("使用过长路径名: %s\n", strerror(errno));
    }
    
    // 示例6: 实际应用场景
    printf("\n示例6: 实际应用场景\n");
    
    // 场景1: 日志文件轮转
    printf("场景1: 日志文件轮转\n");
    const char *log_file = "application.log";
    const char *old_log_file = "application.log.1";
    
    // 创建日志文件
    fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, 0644);
    if (fd != -1) {
        for (int i = 0; i < 10; i++) {
            char log_entry[100];
            sprintf(log_entry, "Log entry %d: Application running normally\n", i);
            write(fd, log_entry, strlen(log_entry));
        }
        close(fd);
        printf("创建日志文件并写入测试数据\n");
    }
    
    // 模拟日志轮转
    if (access(old_log_file, F_OK) == 0) {
        unlink(old_log_file);  // 删除旧的备份
    }
    
    if (rename(log_file, old_log_file) == -1) {
        perror("日志轮转失败");
    } else {
        printf("日志文件轮转成功: %s -> %s\n", log_file, old_log_file);
        
        // 创建新的日志文件
        fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, 0644);
        if (fd != -1) {
            const char *new_log_msg = "New log file created after rotation\n";
            write(fd, new_log_msg, strlen(new_log_msg));
            close(fd);
            printf("创建新的日志文件\n");
        }
    }
    
    // 场景2: 临时文件处理
    printf("场景2: 临时文件处理\n");
    const char *temp_file = "temp_processing.tmp";
    const char *final_file = "final_result.txt";
    
    // 创建临时文件
    fd = open(temp_file, O_CREAT | O_WRONLY, 0644);
    if (fd != -1) {
        const char *temp_content = "Processing result data that needs to be finalized";
        write(fd, temp_content, strlen(temp_content));
        close(fd);
        printf("创建临时处理文件: %s\n", temp_file);
    }
    
    // 处理完成后重命名为最终文件
    if (rename(temp_file, final_file) == -1) {
        perror("重命名临时文件失败");
    } else {
        printf("临时文件处理完成: %s -> %s\n", temp_file, final_file);
    }
    
    // 场景3: 原子性文件更新
    printf("场景3: 原子性文件更新\n");
    const char *config_file = "config.txt";
    const char *temp_config = "config.txt.tmp";
    
    // 创建原始配置文件
    fd = open(config_file, O_CREAT | O_WRONLY, 0644);
    if (fd != -1) {
        const char *old_config = "old_parameter=value1\nold_setting=enabled\n";
        write(fd, old_config, strlen(old_config));
        close(fd);
        printf("创建原始配置文件\n");
    }
    
    // 创建新的配置文件(临时)
    fd = open(temp_config, O_CREAT | O_WRONLY, 0644);
    if (fd != -1) {
        const char *new_config = "new_parameter=value2\nnew_setting=disabled\nupdated=true\n";
        write(fd, new_config, strlen(new_config));
        close(fd);
        printf("创建新配置文件(临时)\n");
    }
    
    // 原子性地替换配置文件
    if (rename(temp_config, config_file) == -1) {
        perror("原子性替换配置文件失败");
    } else {
        printf("原子性更新配置文件成功\n");
        
        // 验证新配置
        printf("更新后的配置文件内容:\n");
        fd = open(config_file, O_RDONLY);
        if (fd != -1) {
            char config_buffer[300];
            ssize_t bytes_read = read(fd, config_buffer, sizeof(config_buffer) - 1);
            if (bytes_read > 0) {
                config_buffer[bytes_read] = '\0';
                printf("%s", config_buffer);
            }
            close(fd);
        }
    }
    
    // 示例7: 跨文件系统操作限制
    printf("\n示例7: 跨文件系统操作限制\n");
    
    // 这个测试需要实际的跨文件系统环境
    printf("说明: rename不能跨文件系统移动文件\n");
    printf("如果尝试这样做,会返回EXDEV错误\n");
    printf("跨文件系统移动需要先复制再删除原文件\n");
    
    // 示例8: 性能考虑
    printf("\n示例8: 性能考虑\n");
    
    // rename操作通常是原子性的且很快
    printf("rename操作的特点:\n");
    printf("  - 原子性: 操作要么完全成功,要么完全失败\n");
    printf("  - 高效性: 通常只是修改元数据,不移动实际数据\n");
    printf("  - 限制性: 不能跨文件系统操作\n");
    
    // 清理测试资源
    printf("\n清理测试资源...\n");
    
    // 删除各种测试文件和目录
    const char *files_to_delete[] = {
        "test_rename_new.txt",
        "target_file.txt",
        "source_file.txt",
        "file_to_move.txt",
        "application.log",
        "application.log.1",
        "final_result.txt",
        "config.txt"
    };
    
    for (int i = 0; i < 8; i++) {
        if (access(files_to_delete[i], F_OK) == 0) {
            if (unlink(files_to_delete[i]) == -1) {
                printf("删除文件 %s 失败: %s\n", files_to_delete[i], strerror(errno));
            } else {
                printf("删除文件: %s\n", files_to_delete[i]);
            }
        }
    }
    
    // 删除目录
    if (access("test_rename_new_dir", F_OK) == 0) {
        char dir_file[PATH_MAX];
        snprintf(dir_file, sizeof(dir_file), "%s/content.txt", "test_rename_new_dir");
        unlink(dir_file);
        if (rmdir("test_rename_new_dir") == -1) {
            printf("删除目录失败: %s\n", strerror(errno));
        } else {
            printf("删除目录: test_rename_new_dir\n");
        }
    }
    
    if (access(move_to_dir, F_OK) == 0) {
        char moved_file[PATH_MAX];
        snprintf(moved_file, sizeof(moved_file), "%s/file_to_move.txt", move_to_dir);
        unlink(moved_file);
        if (rmdir(move_to_dir) == -1) {
            printf("删除目标目录失败: %s\n", strerror(errno));
        } else {
            printf("删除目录: %s\n", move_to_dir);
        }
    }
    
    return 0;
}