C語言實現cp -r --parents拷貝檔案和資料夾
阿新 • • 發佈:2019-02-07
linux下cp -r --parents為回溯的拷貝資料夾,同時複製時保留檔案的目錄結構。下面用posix標準的C語言來實現它。
filetype函式用來檢測指定目錄下面的檔案是否存在,如果存在的話,是那種型別的檔案。函式返回'n'表示檔案不存在,返回b,c,d,p,l,f,s,u分別表示block檔案,字元裝置檔案,資料夾,管道,連結,常規檔案,socket檔案以及未知型別檔案。程式碼如下:
char filetype(char * file_name){ if(access(file_name,R_OK)!=0){ printf("file %s not exist!",file_name); return 'n'; }else{ struct stat filestat={}; int return_state=stat(file_name,&filestat); if(return_state<0){ perror("stat"); exit(1); } switch (filestat.st_mode & S_IFMT) { case S_IFBLK: printf("%s:block device\n",file_name); return 'b'; case S_IFCHR: printf("%s:character device\n",file_name); return 'c'; case S_IFDIR: printf("%s:directory\n",file_name); return 'd' ; case S_IFIFO: printf("%s:FIFO/pipe\n",file_name); return 'p'; case S_IFLNK: printf("%s:symlink\n",file_name); return 'l'; case S_IFREG: printf("%s:regular file\n",file_name); return 'f'; case S_IFSOCK: printf("%s:socket\n",file_name); return 's'; default: printf("%s:unknown?\n",file_name); return 'u'; } } }
copy_file_posix函式是linux c語言的單個檔案複製函式,使用open,write函式,符合posix標準。
void copy_file_posix(char* source_path,char *destination_path){ char buffer[1024]; int fdin,fdout; fdin=open(source_path,O_RDONLY); if(fdin<0){ perror("open"); exit(1); } fdout=open(destination_path,O_CREAT|O_WRONLY,777); if(fdout<0){ perror("open"); exit(1); } int len; while((len=read(fdin,buffer,sizeof(buffer)))>0){ write(fdout,buffer,len); } close(fdout); close(fdin); }
同時它的相似實現copy_file_ansi標準函式如下,其中使用了fopen,fwrite等,程式碼如下:
void copy_file_ansi(char* source_path,char *destination_path){ char buffer[1024]; FILE *in,*out; if((in=fopen(source_path,"r"))==NULL){ perror("fopen"); exit(1); } if((out=fopen(destination_path,"w"))==NULL){ perror("fopen"); exit(1); } int len; while((len=fread(buffer,1,1024,in))>0){ fwrite(buffer,1,len,out); } fclose(out); fclose(in); }
複製資料夾不可避免的需要建立資料夾,下面的函式可以建立單個資料夾,也可以建立多級資料夾,實現了mkdir -p功能。
int make_dir_recursively(char* name){ const char * split = "/"; char * p; p = strtok(name,split); char basepath[1024]=""; while(NULL!=p) { sprintf(basepath, "%s%s/", basepath,p); printf("create dir :%s\n",basepath); if(mkdir(basepath,0777)<0){ if(EEXIST!=errno){ perror("mkdir"); return -1; } } p = strtok(NULL,split); } return 0; }
下面幾個函式都是工具函式,用法和Java同名函式相同。
int lastIndexOf(char *source_str,char *target_str)
{
char *p=source_str;
int i=0,len=strlen(target_str);
p=strstr(source_str,target_str);
if(p==NULL)return -1;
while(p!=NULL)
{
for(;source_str!=p;source_str++)i++;
p=p+len;
p=strstr(p,target_str);
}
return i;
}
void substring(char * src,char * dest,int start_index,int len)
{
if(start_index > strlen(src))
return ;
len = (strlen(src) - start_index) > len ? len:(strlen(src) - start_index);
printf("len:%d\n",len);
int slowwalker=0,i;
for(i=start_index;i<start_index+len;i++,slowwalker++)
{
dest[slowwalker]=src[i];
}
dest[slowwalker]='\0';
}
下面的函式將帶目錄的檔案格式的目錄和檔名稱分隔開,例如檔案hello/world/a.txt,我們將這個字串分割為"hello/world/"和"a.txt"。
void split_path_and_name(char path[],char basepath[],char filename[]){
int index=lastIndexOf(path,"/");
if(index<0){
printf("path %s incorrect",path);
}
substring(path,basepath,0,index);
substring(path,filename,index+1,strlen(path));
}
以下兩個函式是拷貝資料夾的主題,其中copy_dir主要用於複製檔案的準備工作,例如複製的源目錄和目標目錄,如果源目錄是一個檔案會怎麼處理,如果源目錄不存在會怎麼處理,如果源目錄是一個資料夾會怎麼處理,如果目標目錄是一個檔案會怎麼處理,如果目標目錄不存在會怎麼處理,如果目標目錄和源目錄包含目錄分隔符"/"又該怎麼處理等。裡面情況太多,寫到最後越寫越迷糊,整個程式就下面的兩個函式欠考慮的情況很多,BUG也很多,請輕拍。
void walk_dir(char *dir,char *dest_path)
{
char name[MAX_PATH];
char tmp[512]="";
struct dirent *dp;
DIR *dfd;
if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0)
continue; /* skip self and parent */
if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
fprintf(stderr, "dirwalk: name %s %s too long\n",
dir, dp->d_name);
else {
if(lastIndexOf(dir,"/")==(strlen(dir)-1)){
sprintf(name, "%s%s", dir, dp->d_name);
}else{
sprintf(name, "%s/%s", dir, dp->d_name);
}
char tmp[512]="";
sprintf(tmp,"%s%s/%s",tmp,dest_path,name);
if(filetype(name)=='d'){
make_dir_recursively(tmp);
}else{
copy_file_posix(name,tmp);
}
}
}
closedir(dfd);
}
void copy_dir(char* source_path,char *destination_path){
int is_source_directory=0;
int dest_file_status=0;
if(filetype(source_path)=='n'){
printf("file %s not exist!\n",source_path);
exit(1);
}else if(filetype(source_path)=='d'){
is_source_directory=1;
}
printf("is_source_directory:%d\n",is_source_directory);
if(filetype(destination_path)=='n')
{
printf("dest file %s not exist\n",destination_path);
}else if (filetype(destination_path)=='d'){
printf("dest file %s exist,and is a directory\n",destination_path);
dest_file_status=1;
}else{
printf("dest file %s exist,copy failed\n",destination_path);
exit(1);
}
printf("dest_file_status:%d\n",dest_file_status);
char basepath[512]="";
if(is_source_directory==1){
if(lastIndexOf(destination_path,"/")==(strlen(destination_path)-1)){
sprintf(basepath,"%s%s%s",basepath,destination_path,source_path);
printf("copy_dir:mkdir:%s\n",basepath);
}else{
sprintf(basepath,"%s%s/%s",basepath,destination_path,source_path);
printf("copy_dir:mkdir %s\n",basepath);
}
printf("mkdir %s recursively\n",basepath);
make_dir_recursively(basepath);
printf("walk_dir source_path:%s basepath:%s\n",source_path,basepath);
walk_dir(source_path,basepath);
}else{
copy_file_posix(source_path,destination_path);
}
}
最後是main函式,如下所示:
int main(int argc,char ** argv){
if(argc<3){
printf("./a.out source dest");
return 0;
}
copy_dir(argv[1],argv[2]);
}
第一次寫部落格,疏漏很多,同時因為以前主要接觸的是Java程式碼,程式裡面Java經典的駝峰命名法和c/c++的下劃線命名法交替出現,請讀者忽略這些瑕疵,大家一起進步。