Linux 命令 "cp" 代码实现简介
本blog主要是模仿Linux的cp命令的功能,未实现参数,只是基础功能部分。
本文的主要目的在于练习 文件流 和 目录流 中的函数的使用。
主要功能包括两种:
- 源文件属性为文件,拷贝到其它文件(内容复制)或目录(作为目录子文件)
- 源文件属性为目录,拷贝到其它目录(作为子目录存在)
其实现的流程图如下所示:
本copy我们通过三个文件来实现:
- main.c:流程的实现
- copy.c:拷贝功能的实现
- copy.h:必要的头文件
-
main.c
/********************************************************* * File name: * Description: * Author: Jimmy_Nie * Version Modify Time * v1.0 creat 2017-09-12 * *********************************************************/ #include "copy_file.h" int main(int argc, char *argv[]) { //Check the arguments if( 3 != argc) { printf("%s(%d): arguments error!\n",__FILE__, __LINE__); exit(EXIT_FAILURE); } //check the source file is a file or a directory struct stat src_stat; //destination file check struct stat dest_stat; //If source is a file if( FILES == check_file(argv[1], &src_stat) ) { FILE_ENUM dest_enum; dest_enum = check_file( argv[2], &dest_stat ); char cmd[100] = ""; char ch; switch(dest_enum) { case NOT_EXIST: sprintf(cmd, "touch %s", argv[2]); system( cmd ); check_file( argv[2], &dest_stat ); cp_file(argv[1], argv[2], &dest_stat); break; case DIRECTORY: cp_file(argv[1], argv[2], &dest_stat); break; case FILES: fprintf(stdout, "Overwrite the dest file %s, [y/n]\n", argv[2]); ch = getchar(); if( ch == 'Y' || ch == 'y' ) { cp_file(argv[1], argv[2], &dest_stat); } else exit(0); break; default: fprintf(stderr, "%s(%d): file type error\n", __FILE__, __LINE__); } } //If source file is a directory else if( DIRECTORY == check_file(argv[1], &dest_stat) ) { FILE_ENUM dest_enum; dest_enum = check_file( argv[2], &dest_stat ); char cmd[100] = ""; switch(dest_enum) { case NOT_EXIST: sprintf(cmd, "mkdir -p %s", argv[2]); system( cmd ); cp_dir(argv[1], argv[2] ); break; case DIRECTORY: cp_dir(argv[1], argv[2]); break; case FILES: fprintf(stderr, "Can't copy a directory to a file\n"); exit(EXIT_FAILURE); break; default: fprintf(stderr, "%s(%d): file type error\n", __FILE__, __LINE__); break; } } return 0; }
-
copy.c
#include "copy_file.h" FILE_ENUM check_file(char *var, struct stat *st) { if( stat(var, st) ) //if stat function error(renturn nonzero) { if( ENOENT == errno) //No such file or directory { return NOT_EXIST; } else { perror("stat"); exit(EXIT_FAILURE); } } else // stat() ok, no error { //check file attr(dir or file) if( S_ISDIR(st->st_mode )) return DIRECTORY; else if( S_ISREG(st->st_mode) ) return FILES; else { fprintf(stderr, "%s(%d):file type error", __FILE__ , __LINE__); exit(EXIT_FAILURE); } } } //----------------------------------------------------- int cp_file(char *src_var, char *dest_var, struct stat *st) { FILE *src = NULL; FILE *dest = NULL; if( S_ISREG(st->st_mode) ) //if dest is file { //1. open src and dest file if( NULL == (src = fopen(src_var, "r")) ) { perror("fopen"); exit(EXIT_FAILURE); } if( NULL == (dest = fopen(dest_var, "w+")) ) { perror("fopen"); exit(EXIT_FAILURE); } //2. copy the context from src to dest char buf[1024]; int num; while(1) { // if at the end of file or an error occured if( 1024 != (num = fread(buf, 1,1024, src))) { if( !feof(src)) { perror("fread"); exit(EXIT_FAILURE); } else { fwrite(buf, 1, num, dest); fclose(dest); //3. close dest file break; } } fwrite(buf, 1, 1024, dest); } //3. close src file fclose(src); return 0; } if( S_ISDIR(st->st_mode) ) { char buf[100]=""; //make the relative path to absolute path strncpy(buf, dest_var, sizeof(dest_var)); strcat(buf, src_var); //if dest file doesn't exist, creat it first char cmd[100]=""; sprintf(cmd, "touch %s",buf); system(cmd); struct stat new_dest_stat; if( stat(buf, &new_dest_stat)) { perror("stat"); exit(EXIT_FAILURE); } cp_file(src_var, buf, &new_dest_stat); } return 0; } //---------------------------------------------- //if src file is a dir int cp_dir(char *src, char *dest) { DIR *dirp = NULL; //1. open the dir if( NULL == (dirp = opendir(src)) ) { perror("opendir"); exit(EXIT_FAILURE); } struct dirent *entp = NULL; //2. read the dir while( NULL != (entp = readdir(dirp))) //read the dir context { if( 0 == (strcmp(entp->d_name,"..")) || 0 == (strcmp(entp->d_name, "."))) { continue; } char src_buf[100] = ""; char dest_buf[100] = ""; sprintf(src_buf, "%s/%s\0", src, entp->d_name); sprintf(dest_buf, "%s/%s\0", dest, entp->d_name); struct stat src_stat; if( stat(src_buf,&src_stat) ) { perror("stat"); exit(EXIT_FAILURE); } if( S_ISREG(src_stat.st_mode) ) { cp_file(src_buf, dest_buf, &src_stat); } else if( S_ISDIR(src_stat.st_mode) ) { if( -1 == mkdir(dest_buf, src_stat.st_mode) ) { perror("mkdir"); exit(EXIT_FAILURE); } cp_dir(src_buf, dest_buf); //if subdir, recursive call itself } } return 0; }
-
copy.h
#ifndef _COPY_H_ #define _COPY_H_ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <string.h> #include <dirent.h> #include <stdlib.h> typedef enum { NOT_EXIST=1, DIRECTORY, FILES }FILE_ENUM; extern FILE_ENUM check_file(char *var, struct stat *st); //检查文件类型 extern int cp_file(char *src_var, char *dest_var, struct stat *st); //copy文件 extern int cp_dir(char *src, char *dest); //copy目录 #endif