Linux C实现cp功能


一:背景

看了unix/linux编程实践,跟着书上代码实现了普通文件的拷贝,看到课后习题后需要实现目录之间的拷贝,因此有了本文,我最初实现cp用了180多行代码,后来觉得很多地方可以封装,但是最后居然越封装越多达到了200多行,今晚果断再次封装,修剪了代码大概170多行,要比课后答案的要简便点。该cp可以实现普通文件的拷贝,拷贝到指定目录下,和目录直接拷贝等功能。

二:思路

目录之间的拷贝我觉得最主要的功能就是path路径的拼装,处理好path路径问题,就很简单了。例如 /root/a.txt 拷贝到 /tmp下,那么只要传入/root/a.txt 和 /tmp/a.txt即可,那么关键就是/tmp和a.txt的拼装,在拷贝到/tmp/下你也可以先判断下目标是否有相同文件存在。若存在可以提示用户是否覆盖(这个功能我还没做)。这里主要还是字符串处理函数用的比较多,还有一个就是函数返回值得一个难点(下篇博文介绍下函数返回值)。

C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码

读C++ Primer 之构造函数陷阱

读C++ Primer 之智能指针

读C++ Primer 之句柄类

将C语言梳理一下,分布在以下10个章节中:

  1. Linux-C成长之路(一):Linux下C编程概要
  2. Linux-C成长之路(二):基本数据类型
  3. Linux-C成长之路(三):基本IO函数操作
  4. Linux-C成长之路(四):运算符
  5. Linux-C成长之路(五):控制流
  6. Linux-C成长之路(六):函数要义
  7. Linux-C成长之路(七):数组与指针
  8. Linux-C成长之路(八):存储类,动态内存
  9. Linux-C成长之路(九):复合数据类型
  10. Linux-C成长之路(十):其他高级议题

三:实现


#include<stdio.h>

#include<unistd.h>

#include<fcntl.h>

#include<unistd.h>

#include<stdlib.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<string.h>

#include<dirent.h>

#include <libgen.h>

#define BUFFERSIZE 4096

#define COPYMODE 0644

 

char *deal_path(char *,char *);

char *deal_path1(char *,struct dirent *);

int is_file(char *);

char *deal_with(char *,char *);

void oops(char *,char *);

int exists(char *);

void do_cp(char *,char *);

int main(int argc,char *argv[])

{

        char answer[10];

        char c;

        //设置buf

        struct stat filebuf1;

        struct stat filebuf2;

        struct stat tmpbuf;

        char *filename=NULL;

        char *filename2=NULL;

        struct dirent  *dirname;

        DIR *dir_ptr;

        //判断参数

                if(argc != 3){

                fprintf(stderr,"Usage: %s source destination\n",*argv);

                exit(1);

        }

        //判断源文件和目标文件是否相等

        if(strcmp(argv[1],argv[2]) == 0)

        {

                fprintf(stderr,"No Copy Source File equal Dest File\n");

                exit(1);

        }

        //测试文件是否可以访问

 

        if(access(argv[2],F_OK) == 0){

                //询问文件是否可以覆盖

                printf("Can you ovver the file (y/n):");

                scanf("%9s",&answer);

                while((c = getchar()) != EOF && c != '\n');

        }

 

        //判断用户的输入

 

        if(*answer != 'y' && *answer != 'Y'){

                fprintf(stderr,"the dst file exists,don't over\n");

                exit(1);

        }

 

        //判断目标是否是目录

        stat(argv[2],&filebuf2);

        stat(argv[1],&filebuf1);

        if(!S_ISDIR(filebuf1.st_mode)){

                if(S_ISDIR(filebuf2.st_mode))

                        filename = deal_path(argv[2],argv[1]);

                else

                        filename = argv[2];

        //      printf("%s\n",filename);

                do_cp(argv[1],filename);

                free(filename);

            }

             

        else{

                if(S_ISDIR(filebuf2.st_mode))

                if((dir_ptr = opendir(argv[1])) == NULL)

                        sprintf("stderr","Can't open dir %s\n",argv[1]);

                else

                        while((dirname=readdir(dir_ptr)) != NULL){

                                        filename = deal_path(argv[1],dirname->d_name);

                                        if(is_file(filename)){

                                                filename2 = deal_with(filename,argv[2]);

                                                do_cp(filename,filename2);

                                                free(filename);

                                                free(filename2);

                                        }else{

                                                free(filename);

                                                continue;

                                        }

                                }

                        }

                closedir(dir_ptr);

        }

 

 

 

void do_cp(char *path1,char *path2)

{

        int in_fd,out_fd,n_chars;

        char buf[BUFFERSIZE];

        if((in_fd = open(path1,O_RDONLY)) == -1)

                oops("Cannot open",path1);

        if((out_fd = open(path2,O_WRONLY|O_CREAT,COPYMODE)) == -1)

                oops("Cannot create",path2);

        //开始读取

        while((n_chars = read(in_fd,buf,BUFFERSIZE)) > 0)

                if(write(out_fd,buf,n_chars) != n_chars)

                oops("Write error to",path2);

        //判断最后是否写入

        if(n_chars == -1)

                oops("Read error from,path1","");

                        //判断最后是否写入

        if(n_chars == -1)

                oops("Read error from,path1","");

        //关闭文件

        if(close(in_fd) == -1||close(out_fd) == -1)

                oops("Error closing files","");

}

 

 

void oops(char *s1,char *s2)

{

        fprintf(stderr,"Error:%s ",s1);

        perror(s2);

        exit(1);

}

 

 

int exists(char *filename)

{

        return access(filename,F_OK);

 

}

 

int is_file(char *filename)

{

        struct stat filebuf;

        stat(filename,&filebuf);

        if(S_ISREG(filebuf.st_mode))

                return 1;

        else

                return 0;

}

 

char *deal_with(char *filename,char *filename2)

{

        char *file=NULL;;

        if((file = (char *)malloc(strlen(basename(filename))+strlen(filename2)+3)) == NULL)

                perror("malloc error");

                        else{

                if(filename2[strlen(filename2)-1] == '/'){

                        strcpy(file,filename2);

                        strcat(file,basename(filename));

                }else{

                        strcpy(file,filename2);

                        strcat(file,"/");

                        strcat(file,basename(filename));

                    }

        }

        return file;

}

 

char *deal_path(char *file,char *file2)

{

        char *filename=NULL;

        if((filename = (char *)malloc(strlen(file)+strlen(file2)+3)) == NULL)

                                perror("Malloc erro:");

        else{

 

        if(file[strlen(file)-1] == '/'){

                        strcpy(filename,file);

                        strcat(filename,file2);

        }else{

                        strcpy(filename,file);

                        strcat(filename,"/");

                        strcat(filename,file2);

                }

        }

        return filename;

 

}

用的最多的还是这个deal_path这个函数,这个函数会判断/tmp/ /tmp这个的区别,最终会将给出的

两个参数合并成一个文件路径创建并写入要拷贝的内容。

四:总结

C语言基础很重要,最近在看C专家编程,C和指针,感触很深里面很多C语言的细节我都不知道。还有linux C编程实战中的关于结构体的字节对齐我也知之甚少。后期会用博客记录相关内容加深自己对这些内容的理解。

本文永久更新链接地址:

相关内容