一:背景
看了unix/linux编程实践,跟着书上代码实现了普通文件的拷贝,看到课后习题后需要实现目录之间的拷贝,因此有了本文,我最初实现cp用了180多行代码,后来觉得很多地方可以封装,但是最后居然越封装越多达到了200多行,今晚果断再次封装,修剪了代码大概170多行,要比课后答案的要简便点。该cp可以实现普通文件的拷贝,拷贝到指定目录下,和目录直接拷贝等功能。
二:思路
目录之间的拷贝我觉得最主要的功能就是path路径的拼装,处理好path路径问题,就很简单了。例如 /root/a.txt 拷贝到 /tmp下,那么只要传入/root/a.txt 和 /tmp/a.txt即可,那么关键就是/tmp和a.txt的拼装,在拷贝到/tmp/下你也可以先判断下目标是否有相同文件存在。若存在可以提示用户是否覆盖(这个功能我还没做)。这里主要还是字符串处理函数用的比较多,还有一个就是函数返回值得一个难点(下篇博文介绍下函数返回值)。
三:实现
#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编程实战中的关于结构体的字节对齐我也知之甚少。后期会用博客记录相关内容加深自己对这些内容的理解。
本文出自 “专注linux” 博客,请务必保留此出处http://forlinux.blog.51cto.com/8001278/1530293
原文地址:http://forlinux.blog.51cto.com/8001278/1530293