You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言实现mv命令无法跨目录移动文件的修复咨询

如何修改Linux系统调用实现的mv函数以支持跨目录移动?

我用Linux系统调用写了一个实现mv命令的C语言函数,但目前只能完成文件重命名,没法把文件移到其他目录——文件还是留在原目录里。以下是我的实现代码:

void moveFile(char *sourcePath, char *destinationPath) {
    char *error;
    /* determine if Source Path exists */
    if(access(sourcePath, F_OK) != 0) {
        error = "mv: cannot stat '";
        write(2, error, strlen(error));
        write(2, sourcePath, strlen(sourcePath));
        write(2, "': No such file or directory\n", strlen("': No such file or directory\n"));
    }
    /* determine if Source Path and Destination Path are identical */
    if(strcmp(sourcePath, destinationPath) == 0) {
        write(2, "mv: ", strlen("mv: "));
        write(2, sourcePath, strlen(sourcePath));
        write(2, " and ", strlen(" and "));
        write(2, destinationPath, strlen(destinationPath));
        write(2, " are the same\n", strlen(" are the same\n"));
    } else {
        /* rename or move the file from sourcePath to destinationPath */
        int ret_val = rename(sourcePath, destinationPath);
        if(ret_val != 0) {
            write(2, "move unsuccessful\n", strlen("move unsuccessful\n"));
        }
    }
}

请问怎么修改这段代码,让它支持跨目录移动文件?


问题原因分析

你当前的实现只依赖了rename()系统调用,但**rename()只能在同一个文件系统内完成重命名/移动操作**。当源文件和目标目录不在同一个文件系统时(比如跨不同挂载分区),rename()会调用失败,这就是你的代码无法跨目录移动的核心原因。

要实现真正的跨目录(跨文件系统)移动,我们需要模拟原生mv命令的逻辑:先复制源文件到目标路径,再删除源文件。

修改后的完整代码

下面是修改后的实现,既保留了rename()在同文件系统下的高效性,又新增了跨文件系统时的复制+删除逻辑:

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

#define BUFFER_SIZE 4096

void moveFile(char *sourcePath, char *destinationPath) {
    char *error;
    struct stat srcStat, destStat;

    // 检查源文件是否存在(用stat替代access,顺便获取文件属性)
    if(stat(sourcePath, &srcStat) != 0) {
        error = "mv: cannot stat '";
        write(2, error, strlen(error));
        write(2, sourcePath, strlen(sourcePath));
        write(2, "': No such file or directory\n", strlen("': No such file or directory\n"));
        return;
    }

    // 检查源路径和目标路径是否相同
    if(strcmp(sourcePath, destinationPath) == 0) {
        write(2, "mv: ", strlen("mv: "));
        write(2, sourcePath, strlen(sourcePath));
        write(2, " and ", strlen(" and "));
        write(2, destinationPath, strlen(destinationPath));
        write(2, " are the same\n", strlen(" are the same\n"));
        return;
    }

    // 优先尝试rename,同文件系统下原子操作效率高
    int ret_val = rename(sourcePath, destinationPath);
    if(ret_val == 0) {
        return; // 移动成功直接返回
    }

    // 判断rename失败原因:如果是跨设备错误(EXDEV),则走复制删除流程
    if(errno != EXDEV) {
        // 其他错误,输出具体信息
        error = "mv: cannot move '";
        write(2, error, strlen(error));
        write(2, sourcePath, strlen(sourcePath));
        write(2, "' to '", strlen("' to '"));
        write(2, destinationPath, strlen(destinationPath));
        write(2, "': ", strlen("': "));
        write(2, strerror(errno), strlen(strerror(errno)));
        write(2, "\n", 1);
        return;
    }

    // 跨文件系统场景:打开源文件
    int srcFd = open(sourcePath, O_RDONLY);
    if(srcFd == -1) {
        error = "mv: cannot open source file '";
        write(2, error, strlen(error));
        write(2, sourcePath, strlen(sourcePath));
        write(2, "': ", strlen("': "));
        write(2, strerror(errno), strlen(strerror(errno)));
        write(2, "\n", 1);
        return;
    }

    // 创建目标文件,继承源文件的权限
    int destFd = open(destinationPath, O_WRONLY | O_CREAT | O_TRUNC, srcStat.st_mode);
    if(destFd == -1) {
        error = "mv: cannot create destination file '";
        write(2, error, strlen(error));
        write(2, destinationPath, strlen(destinationPath));
        write(2, "': ", strlen("': "));
        write(2, strerror(errno), strlen(strerror(errno)));
        write(2, "\n", 1);
        close(srcFd);
        return;
    }

    // 缓冲区循环复制文件内容
    char buffer[BUFFER_SIZE];
    ssize_t bytesRead;
    while((bytesRead = read(srcFd, buffer, BUFFER_SIZE)) > 0) {
        ssize_t bytesWritten = write(destFd, buffer, bytesRead);
        if(bytesWritten != bytesRead) {
            error = "mv: error writing to destination file\n";
            write(2, error, strlen(error));
            close(srcFd);
            close(destFd);
            unlink(destinationPath); // 清理未完成的目标文件
            return;
        }
    }

    // 检查读取源文件时是否出错
    if(bytesRead == -1) {
        error = "mv: error reading source file '";
        write(2, error, strlen(error));
        write(2, sourcePath, strlen(sourcePath));
        write(2, "': ", strlen("': "));
        write(2, strerror(errno), strlen(strerror(errno)));
        write(2, "\n", 1);
        close(srcFd);
        close(destFd);
        unlink(destinationPath);
        return;
    }

    // 复制完成后关闭文件句柄,删除源文件
    close(srcFd);
    close(destFd);
    if(unlink(sourcePath) == -1) {
        error = "mv: cannot remove source file '";
        write(2, error, strlen(error));
        write(2, sourcePath, strlen(sourcePath));
        write(2, "': ", strlen("': "));
        write(2, strerror(errno), strlen(strerror(errno)));
        write(2, "\n", 1);
        return;
    }
}

关键改进点

  • 优先使用rename():同文件系统下rename()是原子操作,效率更高且更安全,保留这个逻辑作为最优路径。
  • 处理跨文件系统场景:当rename()返回EXDEV错误时,自动切换到复制+删除的流程,实现跨目录移动。
  • 优化错误提示:替换模糊的错误信息,用strerror(errno)输出具体的系统错误,方便调试。
  • 保留文件权限:创建目标文件时继承源文件的权限,更贴近原生mv的行为。
  • 异常清理机制:复制过程中如果出错,会删除未完成的目标文件,避免残留无效文件。

内容的提问来源于stack exchange,提问作者Hans Prieto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 14:43:11