1:main函数
利用"fgets" 获取文件的命令
调用cmd_handle.c中的cmd_exec(command)主函数
#include "cmd_handle.h"
#include "cmd_cp.h"
#define CMD_SIZE 128
int main(){
char command[CMD_SIZE];
do{
printf("bashshell-->");
//获取命令
fgets(command,CMD_SIZE,stdin);
command[strlen(command)-1]='\0';//将回车符转换\0
if(strcmp(command,"exit")==0){
printf("GoodBye:\n");
exit(EXIT_SUCCESS);
}
//调用cmd_handle模块方法进行命令解析
cmd_exec(command);
}while(1);
return 0;
}
2:cmd_handle.h
构建command_t的结构体
#ifndef __HEAD_CMD_HANDLE_H__
#define __HEAD_CMD_HANDLE_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#define SIZE_NAME 128
#define SIZE_ARGC 10
#define SIZE_PARAM 128
//构建结构体
typedef struct{
//文件名
char cmd_name[SIZE_NAME];
//文件个数
int cmd_argc;
//利用二位数组表示文件的个数
char cmd_param[SIZE_ARGC][SIZE_PARAM];
}command_t;
extern void dispath_command(command_t* );
extern void init_command(command_t*);
extern bool cmd_exec(char*);
extern void parse_command(char*,command_t*);
#endif
3.cmd_handle.c
#include "cmd_handle.h"
#include "cmd_cp.h"
#include "cmd_ls.h"
#define DEBUG
void init_command(command_t* command);
void parse_command(char* command, command_t* cmd);
void show_command(command_t* command){
printf("\n-----------------command-------------------\n");
printf("[DEBUG] command name:%s\n",command->cmd_name);
printf("[DEBUG] command argc:%d\n",command->cmd_argc);
printf("[DEBuG] command params:\n");
for(int i=0;i<(command->cmd_argc);i++){
printf("\tparam <%s>\n",command->cmd_param[i]);
}
printf("-------------------------------------------\n");
}
void dispath_command(command_t* command){
//获取命令
char *cmd_name=command->cmd_name;
if(strcmp(cmd_name,"cp")==0){
operate_cp(command);
}else if(strcmp(cmd_name,"ls")==0){
operate_ls(command);
}
}
//主函数
bool cmd_exec(char* command){
#ifdef DEBUG
printf("[DEBUG] command:<%s>",command);
#endif
if(NULL== command){
printf("command is not empty\n");
return false;
}
command_t cmd;
init_command(&cmd);
//解释命令
parse_command(command,&cmd);
#ifdef DEBUG
show_command(&cmd);
#endif
//获取命令
dispath_command(&cmd);
}
//初始化
void init_command(command_t* command){
memset(command->cmd_name,0,SIZE_NAME);
command->cmd_argc=0;
for(int i=0;i<SIZE_ARGC;i++){
memset(command->cmd_param[i],0,SIZE_PARAM);
}
}
void parse_command(char* command, command_t* cmd){
/*
char command[256]="cp /etc/passwd /mnt/hgfs/VMshare/part5_cp";
char *str=strtok(command," ");
printf("<str>%s\n",str);
while((str=strtok(NULL," "))!=NULL){
printf("str=%s\n",str);
}
*/
//利用空格对命令字符串进行分割,获取命令名称(cp)以及每个地址或者文件
//使用strtok函数
//替换名称
char *cmd_name=strtok(command," ");
strcpy(cmd->cmd_name,cmd_name);
//获取参数
int argc =0;
char *param=NULL;
//通过循环将命令后面的地址或文件获取
while((param=strtok(NULL," "))!=NULL){
strcpy(cmd->cmd_param[argc],param);
argc++;
}
cmd->cmd_argc=argc;
}
~
~
cp部分
1.cmd_cp.h
利用枚举函数分别对不同的文件类型枚举
结构体
src_file_name[SIZE_SRC_FILE_NAME]文件名
#ifndef __HEAD_CMD_CP_H__
#define __HEAD_CMD_CP_H__
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "cmd_handle.h"
#define SIZE_SRC_FILE_NAME 128
//枚举
enum FILE_TYPE{
FT_FILE=1,
FT_DIR,
FT_OTHER,
FT_ERROR=1000
};
typedef struct{
enum FILE_TYPE ft;
char src_file_name[SIZE_SRC_FILE_NAME];
}fileInfo_t;
extern bool file_isExist(char* srcpath);
extern void cmd_cp_parse_srcpath(command_t* command,fileInfo_t* fileInfo);
extern int cmd_cp_dir(fileInfo_t fileInfo,char* dest);
extern enum FILE_TYPE cmd_cp_parse_filetype(fileInfo_t* fileInfo);
extern int cmd_cp_file(fileInfo_t sr,char* dest);
extern void cmd_cp_exec(fileInfo_t fileInfo,command_t* command);
extern int operate_cp(command_t* command);
#endif
2.cmd_cp.c
#include"cmd_cp.h"
#include "cmd_handle.h"
#define DEBUG
int operate_cp(command_t* command){
if(NULL==command){
perror("[DEBUG]command is NULL:");
return -1;
}
//判断参数是否足够(复制需要两个参数)
if((command->cmd_argc)!=2){
perror("[DEBUG]Not enough parameters:");
return -1;
}
fileInfo_t file;
//提取源文件的名称
cmd_cp_parse_srcpath(command,&file);
//判断文件是否存在
if(!file_isExist(file.src_file_name)){
printf("Exist is null");
return -1;
}
//提取文件类型
enum FILE_TYPE ft = cmd_cp_parse_filetype(&file);
if(ft == FT_ERROR){
return -1;
}
file.ft=ft;
#ifdef DEBUG
printf("-----------------file------------------\n");
printf("[DEBUG] filetype:%d\t",file.ft);
printf("[DEBUG] src_file_name %s\n",file.src_file_name);
printf("---------------------------------------\n");
#endif
cmd_cp_exec(file,command);
return 0;
}
//判断文件是否存在
bool file_isExist(char* srcpath){
if(NULL == srcpath){
perror("[DEBUG]srcpath is empty\n");
return false;
}
return((access(srcpath,F_OK))==0)?true:false;
}
//提取源文件的名称
void cmd_cp_parse_srcpath(command_t* command,fileInfo_t* fileInfo){
//拿到文件名
strcpy(fileInfo->src_file_name,command->cmd_param[0]);
//打印文件名
#ifdef DEBUG
printf("[DEBUG] src path:%s\n",fileInfo->src_file_name);
#endif
}
//提取文件类型
enum FILE_TYPE cmd_cp_parse_filetype(fileInfo_t* fileInfo){
struct stat statbuf;
int ret =stat(fileInfo->src_file_name,&statbuf);
if(ret != 0){
perror("statbuf");
return FT_ERROR;
}
mode_t mode =statbuf.st_mode;
if(S_ISREG(mode)){
return FT_FILE;
}else if(S_ISDIR(mode)){
return FT_DIR;
}else{
return FT_OTHER;
}
}
void cmd_cp_exec(fileInfo_t fileInfo,command_t* command){
enum FILE_TYPE ft =fileInfo.ft;
switch(ft){
case FT_FILE :
cmd_cp_file(fileInfo,command->cmd_param[1]);
break;
case FT_DIR :
cmd_cp_dir(fileInfo,command->cmd_param[1]);
break;
}
}
//进行文件复制
int cmd_cp_file(fileInfo_t sr,char* dest){
//读文件
FILE* srcfile=fopen(sr.src_file_name,"r");
//写文件
FILE* destfile=fopen(dest,"w+");
if(NULL == srcfile || NULL == destfile){
perror("[DEBUG] stcfile and destfile:");
return -1;
}
//开始循环
char buf[512]={0};
ssize_t num;
while((num =fread(buf,1,sizeof(buf),srcfile))!=0){
fwrite(buf,1,num,destfile);
}
fclose(srcfile);
fclose(destfile);
return 0;
}
int cmd_cp_dir(fileInfo_t fileInfo,char* dest){
//递归终止条件
//意思是如果目标是文件 那就进行文件复制(也是终止条件)
if(fileInfo.ft ==FT_FILE){
return cmd_cp_file(fileInfo,dest);
}
//进行递归操作
//1.打开目录opendir
// DIR *opendir(cint cmd_cp_dir(fileInfo_t fileInfo,char* dest)onst char *name);
DIR* dir = opendir(fileInfo.src_file_name);
if(NULL == dir){
perror("opendir");
return -1;
}
//2.生成子目录
char* newSrcPath = strcat(fileInfo.src_file_name,"/");
char* newDestPath = strcat(dest,"/");
//3.判断目标目录是否存在
if(access(newDestPath,F_OK) == -1){
//目录不存在 尝试创建
if(mkdir(newDestPath,0777) == -1){
perror("mkdir failed");
return -1;
}
}
//4.读取目录的内容
struct dirent* dirent;
while((dirent=readdir(dir))!=NULL){
if(strcmp(dirent->d_name,".")==0 || strcmp(dirent->d_name,"..")==0){
continue;
}
char curSrcPath[512]={0};
strcpy(curSrcPath,newSrcPath);
char curDestPath[512]={0};
strcpy(curDestPath,newDestPath);
//进行拼接
strcat(curSrcPath,dirent->d_name);
strcat(curDestPath,dirent->d_name);
fileInfo_t fi;
strcpy(fi.src_file_name,curSrcPath);
fi.ft =cmd_cp_parse_filetype(&fi);
cmd_cp_dir(fi,curDestPath);
}
return 0;
}
ls部分
1.cmd_ls.h
#ifndef __HEAD_CMD_LS_H__
#define __HEAD_CMD_LS_H__
#pragma once
#include "cmd_handle.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
//getgrgid 函数 头文件
#include <grp.h>
//getpwuid 函数 头文件
#include <pwd.h>
#define SZ_NAME 64
#define SZ_PERMISSION 10
#define SZ_TIME 64
#define SZ_LINK_CONTENT 128
#define SZ_PATH 64
//文件属性的结构体
typedef struct{
struct stat f_attr_stat; //系统原有的info
char f_attr_type;//文件类型
char f_attr_uname[SZ_NAME];//文件的所属者
char f_attr_gname[SZ_NAME];//文件所属组
char f_attr_mtime[SZ_TIME];//最后修改时间
char f_attr_permission[SZ_PERMISSION];//文件权限
char f_attr_name[SZ_NAME]; // 文件名
char f_atter_link_content[SZ_LINK_CONTENT];//软链接文件名
}file_attribute_t;
extern int operate_ls(command_t* command);
//ls命令的总入口
extern int cmd_ls_execute(command_t*);
//遍历目录
extern int cmd_list_directory(char*);
extern int get_file_attr(file_attribute_t* fileAttr,char* path,const char* filename,int isSymbolicLink);
extern void make_path(char* path, const char* dest);
extern void show_file_attribute(file_attribute_t* fileAttr);
extern int getchar_file_type(file_attribute_t* fileAttr);
extern void get_file_uname(file_attribute_t* fileAttr);
extern void get_file_gname(file_attribute_t* fileAttr);
extern void show_file_attribute(file_attribute_t* fileAttr);
extern void get_file_last_modify_time(file_attribute_t* fileAttr);
extern void get_file_symbolic_link_name(char* path,file_attribute_t* fileAttr);
extern int get_file_permission(file_attribute_t* fileAttr);
#endif
2.cmd_ls.c
#include "cmd_ls.h"
#include "cmd_handle.h"
#define DEBUG
int operate_ls(command_t* command)
{
//判断是否为空
if(NULL == command){
perror("[DEBUG] command is NULL");
return -1;
}
//调用cmd_ls模块
if(cmd_ls_execute(command)==-1)
{
printf("ls failed.\n");
return -1;
}
}
int cmd_ls_execute(command_t* command)
{
if(NULL==command){
printf("command is NULL.\n");
return -1;
}
//判断参数的个数(ls -l)
if((command->cmd_argc)==1)
{
strcpy(command->cmd_param[1],".");
}else if(command->cmd_argc!=2){
printf("Command parameter error.\n");
return -1;
}
//遍历目录函数cmd_list_directory
return cmd_list_directory(command->cmd_param[1]);
}
//遍历目录
int cmd_list_directory(char* dirpath)
{
//打开目录
DIR* dir = opendir(dirpath);
if(NULL == dir){
perror("open dir");
return -1;
}
//遍历目录
file_attribute_t fileAttr;
char path[SZ_PATH]={0};
struct dirent* pdirent = NULL;
while ((pdirent =readdir(dir))!=NULL)
{
//剔除(./)与(../)两个目录
if((strcmp(pdirent->d_name,".")==0) || (strcmp(pdirent->d_name,"..")==0)){
continue;
}
/* #ifdef DEBUG
printf("[DEBUG] filename: <%s>.\n",pdirent->d_name);
#endif
*/ //创建空间
memset(&fileAttr,0,sizeof(fileAttr));
make_path(path,dirpath);
get_file_attr(&fileAttr,path,pdirent->d_name,pdirent->d_type==DT_LNK);
}
closedir(dir);
return 0;
}
//拼接路径
void make_path(char* path, const char* dest){
//复制
strcpy(path,dest);
//拼接
strcat(path,"/");
}
//获取文件属性
int get_file_attr(file_attribute_t* fileAttr, char* path, const char* filename, int isSymbolicLink)
{
int ret=-1;
if(isSymbolicLink)
{
ret = lstat(strcat(path,filename),&fileAttr->f_attr_stat);
}
else
{
ret = stat(strcat(path,filename),&fileAttr->f_attr_stat);
}
if(ret == -1)
{
perror("stat");
return -1;
}
// 获取文件类型
if(getchar_file_type(fileAttr)==-1)
{
printf("get file type failed.\n");
return -1;
}
show_file_attribute(fileAttr);
//获取文件权限
if(get_file_permission(fileAttr)==-1)
{
printf("get file permission failed\n");
return -1;
}
get_file_uname(fileAttr);
get_file_gname(fileAttr);
get_file_last_modify_time(fileAttr);
//复制文件名称
strcpy(fileAttr->f_attr_name,filename);
get_file_symbolic_link_name(path,fileAttr);
show_file_attribute(fileAttr);
get_file_last_modify_time(fileAttr);
return 0;
}
//显示文件属性信息
void show_file_attribute(file_attribute_t* fileAttr){
printf("%c",fileAttr->f_attr_type);
printf("%s ",fileAttr->f_attr_permission);
printf("%ld ",fileAttr->f_attr_stat.st_nlink);
printf("%s ",fileAttr->f_attr_uname);
printf("%s ",fileAttr->f_attr_gname);
printf("%ld ",fileAttr->f_attr_stat.st_size);
printf("%s ",fileAttr->f_attr_mtime);
if(fileAttr->f_attr_type=='l')
{
printf("%s-->%s",fileAttr->f_attr_name,fileAttr->f_atter_link_content);
}else
{
printf("%s",fileAttr->f_attr_name);
}
putchar('\n');
}
// 获取文件类型
int getchar_file_type(file_attribute_t* fileAttr)
{
if(NULL == fileAttr)
{
return -1;
}
mode_t mode = fileAttr->f_attr_stat.st_mode;
switch(mode & S_IFMT)
{
case S_IFDIR:
fileAttr->f_attr_type='d';
break;
case S_IFREG:
fileAttr->f_attr_type='-';
break;
case S_IFBLK:
fileAttr->f_attr_type='b';
break;
case S_IFSOCK:
fileAttr->f_attr_type='s';
break;
case S_IFLNK:
fileAttr->f_attr_type='l';
break;
case S_IFIFO:
fileAttr->f_attr_type='p';
break;
case S_IFCHR:
fileAttr->f_attr_type='c';
break;
default:
mode_t mode = fileAttr->f_attr_stat.st_mode;
break;
}
return 0;
}
//获取文件权限
int get_file_permission(file_attribute_t* fileAttr)
{
if(NULL == fileAttr)
{
return -1;
}
//在per数组中放入三个权限
char perm[]={'r','w','x'};
int index= 0;//索引从0开始,为文件类型
//st_mode 是 struct stat 结构体中的一个成员,它表示文件的类型和权限
mode_t mode =fileAttr->f_attr_stat.st_mode;
//8种类型
for(int i=8;i>=0;i--){
if((mode>>i) & 1)
{
fileAttr->f_attr_permission[index]=perm[index%3];//每三次一循环,分别给予权限
}else{
fileAttr->f_attr_permission[index]='-';//普通文件
}
index++;
}
fileAttr->f_attr_permission[index]='\0';//循环结束 给结构体文件权限是\0结束
return 0;
}
//获取所属用户名的方法get_file_uname
void get_file_uname(file_attribute_t* fileAttr)
{
struct passwd* pwd = getpwuid(fileAttr->f_attr_stat.st_uid);
strcpy(fileAttr->f_attr_uname,pwd->pw_name);
}
//获取所属组名的方法get_file_gname
void get_file_gname(file_attribute_t* fileAttr)
{
struct group* group = getgrgid(fileAttr->f_attr_stat.st_gid);
strcpy(fileAttr->f_attr_gname,group->gr_name);
}
//显示最后文件修改时间
void get_file_last_modify_time(file_attribute_t* fileAttr)
{
char* mtimestr=ctime(&fileAttr->f_attr_stat.st_mtim.tv_sec);
//将回车符换为\0
mtimestr[strlen(mtimestr)-1]='\0';
strcpy(fileAttr->f_attr_mtime,mtimestr);
}
//获取软连接名称
void get_file_symbolic_link_name(char* path,file_attribute_t* fileAttr)
{
if(fileAttr->f_attr_type=='l')
{
readlink(path,fileAttr->f_atter_link_content,SZ_LINK_CONTENT);
}
}
Makefile部分
.PHONY: cleanall
TAR = bashshell
RMRF = rm -rf
GCC = gcc
# 查找当前目录下所有的.c文件
SRC = $(wildcard *.c)
# 将.c文件替换为.o文件,生成对象文件列表
OBJS = $(patsubst %.c,%.o,$(SRC))
# 链接所有对象文件生成可执行文件
$(TAR) : $(OBJS)
$(GCC) $^ -o $@
@echo "Compilation successful. Executable: $(TAR)"
# 编译单个.c文件生成.o文件
%.o : %.c
$(GCC) -c $< -o $@
# 清理目标,删除所有.o文件和可执行文件
cleanall:
$(RMRF) *.o $(TAR)
@echo "Cleanup completed."