C语言实现简单版的Linux的cp命令
前言:
这是我的第一篇CSDN文章,写的不够好的地方有请各路大神指正。本人也是接触代码时间很短,在这里希望把自己学习到的知识写成一些有用的代码。后面有空的话还会继续多写写这类的文章。
不想看文章写了啥的同鞋请直接拉到最下面拉代码。
一、通过主函数传参实现cp命令参数的传入
(1)int argc
这个参数记录着有多少个主函数传参,数值为实际需要的传参数加1而cp命令需要两个参数(不考虑-r -f等等参数),分别是复制的源路径以及复制的目标路径,所以用判断是否输入了足够的参数。
(2)char ** argv
这个二级指针则是字符串数组,可以写成char * argv[]。从argv[1]开始是第一个传参,argv[2]是第二个传参。。。而argv[0]是执行文件的名字。
二、通过open函数打开对应路径,判断该路径是普通文件还是目录文件
对应函数:get_cp_status(const char * path , int flag);
函数里面利用open函数打开path,如果打开失败,则判断errno错误码,当errno等于EISDIR时,是目录,返回0,如果不是则perror输出错误提示,并返回-1。如果打开成功,返回打开成功的文件描述符
flag是打开文件的权限,对应有三个宏定义:FILE_RDWR(可读可写),FILE_CREATE(不存在则创建),FILE_TUNC(存在则清空),当多个参数需要使用的时候用+连接。
对argv[1]和argv[2]都进行判断并打开,并获得返回值Path1_fd,Path2_fd作后面区分以及复制使用。
三、根据Path1_fd,Path2_fd判断复制方式并进行复制
(1)Path1_fd,Path2_fd均大于0
对应函数:fn_Cp_File_to_File(int fd1 , int fd2);
证明两个主函数传参都是文件的路径。那么此时Path1_fd,Path2_fd均是文件描述符,只需要使用read函数读Path1_fd,然后把读到的内容放在buf里面,然后用write函数往Path2_fd里面写buf的内容即可。
(2)Path1_fd大于0 ,Path2_fd等于0
对应函数:fn_Cp_File_to_Dir(const char * file_path ,const char * dir_path);
此时Path1_fd是文件,Path2_fd是目录,把文件复制到指定目录中。该函数的传参1是需要复制的文件路径,传参2是目录路径,他们可以是我们的两个主函数传参。
这时候需要在 Path2对应的目录中创建一个新的,与Path1文件名相同的文件并且打开获取其文件描述符,此时需要用到open函数,mode传参需要设置成O_RDWR | O_CREAT | O_TRUNC,可读可写,不存在则创建(对应在path2目录中没有这个文件),存在则清空(对应在path2目录中已经存在这个文件)。
由于path1是一个路径,则编写函数get_name把path1中的文件名提取出来。主要思路就是用strtok库函数对path1路径根据”/”进行切割。每次切割的字符串保存在堆空间中,当strtok函数返回值为NULL,此时堆空间的字符串就是path1的文件名。
最后重新根据path1文件的路径用open函数重新打开复制源文件,配合已经创建好的,而且在path2里面的,一个待复制的文件,使用fn_Cp_File_to_File函数复制。
(3)Path1_fd,Path2_fd均等于0
对应函数:fn_Cp_Dir_to_Dir(const char * dir_path1 ,const char * dir_path2);
代表此时的复制是把一个目录复制到另外一个目录中去。(难点)
1.获得当前工作路径:
当主函数传参为相对路径的时候(不以”/”, ”~” 开头的路径),获取源文件夹以及目标文件夹的源路径的时候需要先切换回当前工作路径,在进入下一个路径。
2.获得源文件夹以及目标文件夹的绝对路径
当路径为绝对路径时,传参路径就是文件夹的路径。当为相对路径的时候,需要进入该路径,然后使用getcwd函数获得文件夹的绝对路径。
3.在目标文件夹里面创建对应的目录
使用前面的get_name函数获得path1路径文件夹的名字,在path2路径下创建使用mkdir函数对应文件夹。并且使用sprintf函数把目标文件夹绝对路径和目录名拼接起来。
使用readdir函数读取源文件夹的文件名,当读到非目录的时候:把源文件夹绝对路径与该文件名拼接。此时就可以调用前面写好的fn_Cp_File_to_Dir函数把文件复制到指定目录中。
当读到的是一个目录的时候,使用sprintf函数把该目录名与目标文件夹路径拼接起来,以及用sprintf函数把该目录名与源目标文件夹路径拼接起来。这两个路径作为fn_Cp_Dir_to_Dir函数传参,递归调用本函数继续进行复制。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#define FILE_RDWR 1
#define FILE_CREATE 2
#define FILE_TUNC 4
static int get_cp_status(const char * path , int flag);
static void fn_Cp_File_to_File(int fd1 , int fd2);
static void fn_Cp_File_to_Dir(const char * file_path ,const char * dir_path);
static void fn_Cp_Dir_to_Dir(const char * dir_path1 ,const char * dir_path2);
static char * get_name(const char * path);
int main(int argc , char **argv)
{
int Path1_fd;
int Path2_fd;
if(argc < 3)
{
printf("请输入足够的参数\n");
return 0;
}
//判断传参路径是什么文件,普通文件则打开,目录文件返回0,返回值是文件描述符
Path1_fd = get_cp_status(argv[1] , FILE_RDWR);
Path2_fd = get_cp_status(argv[2] , FILE_RDWR + FILE_CREATE + FILE_TUNC);
//打开文件出错
if(Path1_fd < 0 || Path2_fd < 0)
{
return 0;
}
//文件间复制
if(Path1_fd > 0 && Path2_fd > 0)
{
fn_Cp_File_to_File(Path1_fd , Path2_fd);
close(Path1_fd);
close(Path2_fd);
}
//文件到目录复制
if(Path1_fd > 0 && Path2_fd == 0)
{
close(Path1_fd);
fn_Cp_File_to_Dir(argv[1] , argv[2]);
}
//目录到目录复制
if(Path1_fd == 0 && Path2_fd == 0)
{
fn_Cp_Dir_to_Dir(argv[1] , argv[2]);
}
return 0;
}
//返回值:0(路径为目录) , 返回值大于0(路径为文件) , 返回-1:打开文件失败
static int get_cp_status(const char * path , int flag)
{
int fd = -1;
if(flag == 7)
{
fd = open(path , O_RDWR | O_CREAT | O_TRUNC);
}
else
{
fd = open(path , O_RDWR);
}
if(fd <= 0)
{
if(errno == EISDIR)
{
return 0;
}
else
{
perror("错误原因");
return -1;
}
}
else
{
return fd;
}
}
static void fn_Cp_File_to_File(int fd1 , int fd2)
{
char tmp_buf[1024] = {0};
while(1)
{
int ret = read(fd1 , tmp_buf , 1024);
if(ret <= 0)
{
break;
}
write(fd2 , tmp_buf , ret);
}
}
static char * get_name(const char * path)
{
char tmp_path[1024] = {0};
char * tmp_p = malloc(1024);
char * p = NULL;
//字符串处理
strcpy(tmp_path , path);
p = strtok(tmp_path , "/");
strcpy(tmp_p , p);
while(1)
{
p = strtok(NULL , "/");
if(p == NULL)
{
break;
}
else
{
strcpy(tmp_p , p);
}
}
return tmp_p;
}
static void fn_Cp_File_to_Dir(const char * file_path ,const char * dir_path)
{
char cp_path[1024] = {0};
int file1_fd = 0;
int file2_fd = 0;
file1_fd = open(file_path , O_RDWR);
//获得目标文件夹路径
chdir(dir_path);
getcwd(cp_path , 1024);
sprintf(cp_path , "%s/%s" , cp_path , get_name(file_path));
file2_fd = open(cp_path , O_RDWR | O_CREAT | O_TRUNC);
fn_Cp_File_to_File(file1_fd , file2_fd);
close(file1_fd);
close(file2_fd);
}
static void fn_Cp_Dir_to_Dir(const char * dir_path1 ,const char * dir_path2)
{
DIR * dp = NULL;
struct dirent * dir_info;
char cp_path_begin[1024] = {0};
char cp_path_from[1024] = {0};
char cp_path_to[1024] = {0};
getcwd(cp_path_begin , 1024);
//获得完整的路径1
if(dir_path1[0] == '~' || dir_path1[0] == '/')
{
strcpy(cp_path_from , dir_path1);
printf("cp_path from:%s\n" , cp_path_from);
}
else
{
chdir(dir_path1);
getcwd(cp_path_from , 1024);
chdir(cp_path_begin);
printf("cp_path from:%s\n" , cp_path_from);
}
//获得完整的路径2
if(dir_path2[0] == '~' || dir_path2[0] == '/')
{
strcpy(cp_path_to , dir_path2);
printf("cp_path to:%s\n" , cp_path_to);
mkdir(cp_path_to , 0777);
}
else
{
chdir(dir_path2);
getcwd(cp_path_to , 1024);
chdir(cp_path_begin);
//创建被复制的目录
sprintf(cp_path_to , "%s/%s" , cp_path_to , get_name(dir_path1));
printf("cp_path to:%s\n" , cp_path_to);
mkdir(cp_path_to , 0777);
}
//开始复制
dp = opendir(cp_path_from);
while(1)
{
dir_info = readdir(dp);
if(dir_info == NULL)
{
break;
}
if(dir_info->d_type == DT_DIR)
{
//排除.和..
if((strcmp(dir_info->d_name , ".") == 0) || (strcmp(dir_info->d_name , "..") == 0))
{
continue;
}
//重新拼接目录
char cp_path_from_r[1024] = {0};
char cp_path_to_r[1024] = {0};
sprintf(cp_path_from_r , "%s/%s" , cp_path_from , dir_info->d_name);
sprintf(cp_path_to_r , "%s/%s" , cp_path_to , dir_info->d_name);
fn_Cp_Dir_to_Dir(cp_path_from_r , cp_path_to_r);
}
else
{
char file_path[1024] = {0};
sprintf(file_path , "%s/%s" , cp_path_from , dir_info->d_name);
fn_Cp_File_to_Dir(file_path , cp_path_to);
}
}
closedir(dp);
return;
}