重新实现cp命令

  • 利用POSIX API(文件操作也可以使用ANSI C标准I/O库)编程实现cp –r命令,支持将源路径(目录)中的所有文件和子目录,以及子目录中的所有内容,全部拷贝到目标路径(目录)中。

  • lUNIX/Linux中在shell中输入命令名(可执行文件名)来启动程序,在命令名(可执行文件名)之后可以跟随一系列字符串(通过空格分割),这些字符串就是命令行参数

    cp [参数] <源文件路径> <目标文件路径>。

    cp /usr/local/src/main.c /root/main.c(文件到文件复制)。

    cp /usr/local/src/main.c /root (文件到目录复制)。

    cp –r /usr/local/src /root(递归复制,用于目录到目录的复制)。

    源代码为:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <dirent.h>
    #include <errno.h>
    #include <string.h>
    #include<libgen.h>
    #define MAX_PATH 1024 //文件路径长度
    #define BUF_SIZE 1024 //一次复制的字符长度
    int filestate; //实际读取的长度
    char outbuf[BUF_SIZE]; //一次复制的字符串缓冲区

    void dircopy(char *s_dir, char *d_dir, char *path);
    void createdir(char *s_dir, char *d_dir, struct stat statbuf);
    void spilt(char *s, char **sp);
    void get_path(char *x_path, char **sp, char **dp);
    void filecopy(char * para_sourcefile,char * para_destfile,mode_t para_mode)

    void filecopy(char * para_sourcefile,char * para_destfile,mode_t para_mode)
    {
    int infd, outfd;
    outfd = open(para_destfile, O_CREAT | O_TRUNC | O_WRONLY, para_mode);
    infd = open(para_sourcefile, O_RDONLY); //分别新建文件和打开源文件

    do //进行复制操作
    {
    filestate = read(infd, outbuf, BUF_SIZE);
    write(outfd, outbuf, filestate);
    } while (filestate != 0);

    close(outfd);
    close(infd);
    }

    void get_path(char *x_path, char **sp, char **dp)
    {

    for (int j = 0; (sp[j] != NULL && dp[j] != NULL); j++)
    {
    if (strcmp(sp[j], dp[j]))
    {
    strcpy(x_path, sp[j]);
    }
    } //找到第一个不同的目录
    }

    void spilt(char *s, char **sp)
    {
    char *p = NULL;
    int i = 0;

    p = strtok(s, "/");
    sp[i] = p;
    i = i + 1;

    while ((p = strtok(NULL, "/")) != NULL)
    {
    sp[i] = p;
    }
    }

    void createdir(char *x_path, char *d_dir, struct stat statbuf)
    {

    char newdir[MAX_PATH] = {};
    strcat(newdir, d_dir);
    strcat(newdir, "/");
    strcat(newdir, x_path);

    //用于生成目录绝对路径

    int i = mkdir(newdir, statbuf.st_mode); //生成目录
    }

    void dircopy(char *s_dir, char *d_dir, char *path) //绝对路径,相对路径。
    {

    DIR *sourcdir; //源目录流
    sourcdir = opendir(s_dir);

    if (sourcdir == NULL)
    {
    printf("源目录打开失败:%s,请检查参数%s\n", strerror(errno), s_dir);
    }

    else
    {
    struct stat statbuf;
    stat(s_dir, &statbuf); //获取当前文件的状态结构
    createdir(path, d_dir, statbuf);

    struct dirent *currentdp;
    while ((currentdp = readdir(sourcdir)) != NULL)

    {
    if (currentdp->d_type == DT_REG)
    {
    char destfile[MAX_PATH] = {}; //目标路径
    char sourcefile[MAX_PATH] = {}; //源路径
    char newdir[MAX_PATH] = {}; //新建目录

    strcat(destfile, d_dir);
    strcat(destfile, "/");
    strcat(destfile, path);
    strcat(destfile, "/");
    strcat(destfile, currentdp->d_name);
    //要生成文件的绝对路径

    strcat(sourcefile, s_dir);
    strcat(sourcefile, "/");
    strcat(sourcefile, currentdp->d_name);
    //源文件绝对路径

    struct stat statbuf1;
    stat(sourcefile, &statbuf1); //获取当前文件的状态结构
    filecopy(sourcefile,destfile,statbuf1.st_mode);

    }
    else if ((currentdp->d_type == DT_DIR) && strcmp(currentdp->d_name, "..") && (strcmp(currentdp->d_name, ".")))
    {
    char sdir[MAX_PATH] = {};

    strcat(sdir, s_dir);
    strcat(sdir, "/");
    strcat(sdir, currentdp->d_name); //获取要复制的源目录绝对路径

    char x_path[MAX_PATH] = {};
    strcat(x_path, path);
    strcat(x_path, "/");
    strcat(x_path, currentdp->d_name); //获取需要复制的目录相对路径

    dircopy(sdir, d_dir, x_path);
    }
    }
    }
    closedir(sourcdir);
    }

    int main(int arg, char *argv[])
    {
    struct stat statbuf2;
    stat(argv[1], &statbuf2);
    //源文件/目录状态参数

    struct stat statbuf3;
    stat(argv[2], &statbuf3);
    //目标文件/目录状态参数

    if (arg != 3)
    {
    printf("命令格式为:mycp [源文件或目录] [目标文件或目录]\n");
    }
    //提示命令正确格式

    else
    {
    DIR *destdir; //目标目录流。
    if (S_ISDIR(statbuf2.st_mode)) //源路径为目录时的操作
    {
    if (!S_ISDIR(statbuf3.st_mode))
    {
    printf("参数错误,找不到目标目录 %s\n", argv[2]);
    exit(0);
    }

    else
    {
    char para_s[MAX_PATH] = {};
    char para_d[MAX_PATH] = {};
    strcpy(para_s, argv[1]);
    strcpy(para_d, argv[2]); //字符串分隔函数传参,防止传址调用改变源值

    char *sp[MAX_PATH] = {NULL};
    char *dp[MAX_PATH] = {NULL}; //存放分隔的字符串,路径深度为MAX_PATH

    spilt(para_s, sp);
    spilt(para_d, dp);
    char x_path1[MAX_PATH] = {};
    get_path(x_path1, sp, dp);

    printf("进入目录递归复制...\n");
    dircopy(argv[1], argv[2], x_path1); //调用目录复制函数
    printf("已将源目录 \"%s\" 递归复制到目标目录 \"%s\" 下\n", argv[1], argv[2]);
    }
    }

    else if (S_ISREG(statbuf2.st_mode))
    { //源路径为普通文件时的操作。

    if (!S_ISDIR(statbuf3.st_mode))
    { //目标路径不是目录

    filecopy(argv[1],argv[2],statbuf2.st_mode);
    printf("已将源文件 \"%s\" 复制到目标文件 \"%s\" \n", argv[1], argv[2]);
    }

    else
    { //目标路径是目录

    destdir = opendir(argv[2]);
    if (destdir == NULL)
    {
    printf("目的目录打开失败:%s,请检查参数%s", strerror(errno), argv[2]);
    }
    else
    {

    char destfile[MAX_PATH] = {}; //存放目的文件路径名,将目录和文件名拼接。
    strcat(destfile, argv[2]);
    strcat(destfile, "/");

    char *temp = NULL;
    temp = basename(argv[1]);

    strcat(destfile, temp);
    filecopy(argv[1],destfile,statbuf2.st_mode);
    closedir(destdir);
    printf("已将源文件 \"%s\" 复制到目标目录 \"%s\" 下\n", argv[1], argv[2]);
    }
    }
    }
    else
    {
    printf("参数错误,找不到源文件或目录: %s\n", argv[1]);
    exit(0);
    }
    }
    return (0);
    }