歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux C實現cp功能

Linux C實現cp功能

日期:2017/3/1 9:41:44   编辑:Linux編程

一:背景

看了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+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm

讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm

讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm

讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm

將C語言梳理一下,分布在以下10個章節中:

  1. Linux-C成長之路(一):Linux下C編程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
  2. Linux-C成長之路(二):基本數據類型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
  3. Linux-C成長之路(三):基本IO函數操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
  4. Linux-C成長之路(四):運算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
  5. Linux-C成長之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
  6. Linux-C成長之路(六):函數要義 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
  7. Linux-C成長之路(七):數組與指針 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
  8. Linux-C成長之路(八):存儲類,動態內存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
  9. Linux-C成長之路(九):復合數據類型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
  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編程實戰中的關於結構體的字節對齊我也知之甚少。後期會用博客記錄相關內容加深自己對這些內容的理解。

Copyright © Linux教程網 All Rights Reserved