【Linux】C语言实现文件夹拷贝

本文介绍如何使用C语言在Linux环境下复制整个文件夹,包括遍历目录、判断文件与文件夹、复制文件和文件夹等内容。文章详细阐述了复制函数及难点处理,如字符串拼接、排除特定目录等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在《【Linux】利用C语言文件流复制单一文件》(点击打开链接)讲述了如何用C语言拷贝文件,但是这只能拷贝单一文件。如果你要用LinuxC拷贝整个文件夹,同样要像《【Java】利用文件输入输出流完成把一个文件夹内的所有文件拷贝的另一的文件夹的操作》(点击打开链接)一样,先用《【Linux】遍历某一目录,判断文件与文件夹,main参数》(点击打开链接)的方法遍历整个文件目录,之后再一个一个实现文件拷贝。具体代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<dirent.h>//输出文件信息
#include<sys/stat.h>//判断是否目录
int is_dir(char* path){//判断是否是目录
	struct stat st;
	stat(path,&st);
	if(S_ISDIR(st.st_mode)){
		return 1;
	}
	else{
		return 0;
	}
}
/*字符串处理函数*/
int endwith(char* s,char c){//用于判断字符串结尾是否为“/”
	if(s[strlen(s)-1]==c){
		return 1;
	}
	else{
		return 0;
	}
}
char* str_contact(char* str1,char* str2){//字符串连接
	char* result;
	result=(char*)malloc(strlen(str1)+strlen(str2)+1);//str1的长度+str2的长度+\0;
	if(!result){//如果内存动态分配失败
		printf("字符串连接时,内存动态分配失败\n");
		exit(1);
	}
	strcat(result,str1);
	strcat(result,str2);//字符串拼接
	return result;
}
/*复制函数*/
void copy_file(char* source_path,char *destination_path){//复制文件
	char buffer[1024];
	FILE *in,*out;//定义两个文件流,分别用于文件的读取和写入int len;
	if((in=fopen(source_path,"r"))==NULL){//打开源文件的文件流
		printf("源文件打开失败!\n");
		exit(1);
	}
	if((out=fopen(destination_path,"w"))==NULL){//打开目标文件的文件流
		printf("目标文件创建失败!\n");
		exit(1);
	}
	int len;//len为fread读到的字节长
	while((len=fread(buffer,1,1024,in))>0){//从源文件中读取数据并放到缓冲区中,第二个参数1也可以写成sizeof(char)
		fwrite(buffer,1,len,out);//将缓冲区的数据写到目标文件中
	}
	fclose(out);
	fclose(in);
}
void copy_folder(char* source_path,char *destination_path){//复制文件夹
	if(!opendir(destination_path)){
		if (mkdir(destination_path,0777))//如果不存在就用mkdir函数来创建
		{
		    printf("创建文件夹失败!");
		}
	}
	char *path;
	path=(char*)malloc(512);//相当于其它语言的String path="",纯C环境下的字符串必须自己管理大小,这里为path直接申请512的位置的空间,用于目录的拼接
	path=str_contact(path,source_path);//这三句,相当于path=source_path
	struct dirent* filename;
	DIR* dp=opendir(path);//用DIR指针指向这个文件夹
	while(filename=readdir(dp)){//遍历DIR指针指向的文件夹,也就是文件数组。
		memset(path,0,sizeof(path));
		path=str_contact(path,source_path);
		//如果source_path,destination_path以路径分隔符结尾,那么source_path/,destination_path/直接作路径即可 
		//否则要在source_path,destination_path后面补个路径分隔符再加文件名,谁知道你传递过来的参数是f:/a还是f:/a/啊?
		char *file_source_path;
		file_source_path=(char*)malloc(512);
		if(!endwith(source_path,'/')){
			file_source_path=str_contact(file_source_path,source_path);
			file_source_path=str_contact(source_path,"/");
		}
		else{
			file_source_path=str_contact(file_source_path,source_path);
		}
		char *file_destination_path;
		file_destination_path=(char*)malloc(512);
		if(!endwith(destination_path,'/')){
			file_destination_path=str_contact(file_destination_path,destination_path);
			file_destination_path=str_contact(destination_path,"/");
		}
		else{
			file_destination_path=str_contact(file_destination_path,destination_path);
		}
		//取文件名与当前文件夹拼接成一个完整的路径
		file_source_path=str_contact(file_source_path,filename->d_name);
		file_destination_path=str_contact(file_destination_path,filename->d_name);
		if(is_dir(file_source_path)){//如果是目录
			if(!endwith(file_source_path,'.')){//同时并不以.结尾,因为Linux在所有文件夹都有一个.文件夹用于连接上一级目录,必须剔除,否则进行递归的话,后果无法相像
				copy_folder(file_source_path,file_destination_path);//进行递归调用,相当于进入这个文件夹进行复制~
			}		
		}
		else{
			copy_file(file_source_path,file_destination_path);//否则按照单一文件的复制方法进行复制。
			printf("复制%s到%s成功!\n",file_source_path,file_destination_path);
		}
	}	
}
/*主函数*/
int main(int argc,char *argv[]){
	if(argv[1]==NULL||argv[1]==NULL){
		printf("请输入两个文件夹路径,第一个为源,第二个为目的!\n");
		exit(1);
	}
	char* source_path=argv[1];//取用户输入的第一个参数
	char* destination_path=argv[2];//取用户输入的第二个参数
	DIR* source=opendir(source_path);
	DIR* destination=opendir(destination_path);
	if(!source||!destination){
		printf("你输入的一个参数或者第二个参数不是文件夹!\n");
	}
	copy_folder(source_path,destination_path);//进行文件夹的拷贝
	return 0;
}

运行结果如下,设置一个有文件夹,有文件的文件夹A,计划将里面的内容,拷贝到原本空空是也的文件夹B中。


上述程序中,难点有以下几个:

1、由于此程序涉及多次文件路径的拼接,因此最大的难题就是C语言中对于字符串的处理。众所周知,C语言中是没有字符串的概念,仅有字符数组,与指向这个字符数组的首位置的指针的概念。用char *path;配合path=(char*)malloc(512);相当于搞出一个,指向长达512的空字符数组的指针,此时path你可以理解为字符串。这样使用的strcat函数,不停地给path进行拼接,就不会出现段错误的内存溢出错误。

接下去用了大量的代码用于源与目标文件路径与文件名的拼接,其实完成的功能很简单,如果是其它语言,可能string file_source_path;string filename->d_name;之后就一句file_source_path+filename->d_name;

反正大家明白什么回事就行了,不必细究,我也不过是把《【Linux】纯C环境下字符串的处理》(点击打开链接)中方法搞过来。

2、还有一个问题,就是复制的时候,要注意排除当前文件夹中.与..这两个链接到上级目录与根目录的文件夹。在Linux似乎任何一个文件夹都有这个东东,一开始我写程序的时候,根本不知道,搞了一个复杂度超高还根本停不下来的递归迭代,这也是Linux与Windows不一样的地方。

3、其它地方也就没什么了,都是一些之前文件写过的东西。

在C语言实现Linux 文件夹或文件的复制功能,并且让这个程序能够作为一个可以在终端执行的二进制文件,你需要综合运用系统调用、文件操作以及进程管理等技术。这里我们将展示如何创建这样一个简单的工具。 ### 概述 为了完成这项任务,我们需要考虑以下几个方面: - 判断源路径是否有效(存在并且可以访问) - 区分对待普通文件与目录的不同处理逻辑 - 使用递归遍历整个目录树来进行深度拷贝 - 设置适当的权限位保证目标文件拥有正确的属性 - 提供用户友好的命令行界面用于指定参数如`cp [OPTION]... [-T] SOURCE DEST` 接下来是一个基本框架的例子来帮助理解具体的实现细节: #### 示例代码: ```c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <dirent.h> #define BUFFER_SIZE 4096 /* 缓冲区大小 */ /* 把单个文件从 src 路径复制到 dst */ static void copy_file(const char* src, const char* dst) { int in_fd = open(src, O_RDONLY), out_fd; struct stat st; if(in_fd < 0 || fstat(in_fd,&st)<0 ) { perror("Source file error"); exit(-1);} umask(0); // 清除当前用户的掩码以确保原样保留原始模式 out_fd=open(dst,O_WRONLY|O_CREAT|O_TRUNC,st.st_mode & ~umask()); if(out_fd<0){close(in_fd); perror("Destination file creation failed."); exit(-1);} ssize_t nread; char buffer[BUFFER_SIZE]; while((nread=read(in_fd,buffer,BUFFER_SIZE))>0){ write(out_fd,buffer,nread); } close(in_fd); close(out_fd); } /* 复制一个空目录 */ void mkdir_recursive(const char *path) { struct stat path_stat; int exists = stat(path, &path_stat); if (!exists && S_ISDIR(path_stat.st_mode)) return; if(exists == -1 && errno != ENOENT) perror("Directory check error"), exit(errno); size_t len=strlen(path); for(size_t i=1;i<=len;++i){ if (path[i]=='/') { char tmp=i+1<path?path[i]=0,path[path[i++]=0]:0; if(mkdir(path,S_IRWXU)!=0&&errno!=EEXIST)perror("mkdir"),exit(errno); if(tmp)path[i]=tmp; } } if(mkdir(path,S_IRWXU)!=0&&errno!=EEXIST)perror("final mkdir"),exit(errno); } /* 对于给定的src和dst路径,如果它们都是目录,则递归地将所有内容从src复制到dst */ void cp_r(const char* src_path, const char* dest_dir) { DIR *dir; struct dirent *entry; struct stat sb; dir = opendir(src_path); if(dir==NULL) perror("opendir() failed."),exit(EXIT_FAILURE); mkdir_recursive(dest_dir); chdir(src_path); while ((entry=readdir(dir))) { if(strcmp(entry->d_name,".") && strcmp(entry->d_name,"..")) { snprintf(dest_dir,strlen(dest_dir)+strlen("/")+strlen(entry->d_name)+1,"%s/%s",dest_dir, entry->d_name); lstat(entry->d_name,&sb); if(S_ISREG(sb.st_mode)){ printf("Copying %s -> %s\n",entry->d_name,dest_dir ); copy_file(entry->d_name,dest_dir); }else if(S_ISDIR(sb.st_mode)){ mkdir(dest_dir,sb.st_mode); printf("Entering directory '%s'\n",entry->d_name); cp_r(entry->d_name,dest_dir); } memset(&dest_dir[strlen(dest_dir)-strlen("/")-strlen(entry->d_name)], '\0', strlen(entry->d_name)+1); } } closedir(dir); } int main(int argc, char **argv) { if(argc!=3){ fprintf(stderr,"usage:%s source destination\n", argv[0]); exit(EXIT_SUCCESS); } struct stat s; if(stat(argv[1],&s)==-1){ perror("Cannot access source"); exit(EXIT_FAILURE); } if(!S_ISREG(s.st_mode)&&!S_ISDIR(s.st_mode)){ fprintf(stderr,"%s is not a regular file or folder.\n", argv[1]); exit(EXIT_FAILURE); } if(S_ISDIR(s.st_mode)){ cp_r(argv[1],argv[2]); } else { copy_file(argv[1],argv[2]); } puts("\ncopy complete."); return EXIT_SUCCESS; } ``` 上述代码提供了一个简易版本的 `cp -r` 功能,它支持把单一文件或是整个目录结构都完整地转移到另一位置上。此脚本会根据提供的来源检查其类别,并相应选择合适的复制策略。 最后一步是编译该代码成可执行文件形式。你可以通过gcc来做这件事: ```bash $ gcc my_cp.c -o my_cp ``` 这会产生名为`my_cp`的新二进制文件,之后便可以直接在命令提示符下调用它作为常规实用程序之一了。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值