標籤:linux c
一:背景
看了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 0644char *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
linux C實現cp功能