非常详细!Linux系统下实现minishell(含源码)

目录

1. Shell简介

2. 概要设计

2.1 系统设计

2.2 模块介绍

3. 相关模块介绍

4. 相关代码


1. Shell简介

        Shell(壳)是计算机操作系统中提供与用户交互的命令行界面(CLI)或脚本解释器的程序。它是用户与操作系统内核之间的接口,允许用户通过输入命令和参数来执行各种操作。

        Shell通常是作为操作系统的一部分提供的,例如Unix、Linux和macOS等。每个操作系统可能会有不同的Shell实现,但其中最常见的是Bourne Shell(sh)及其衍生版本,如Bash(Bourne Again SHell),以及C Shell(csh)和Korn Shell(ksh)。还有其他一些流行的Shell,如Zsh(Z Shell)和Fish(Friendly Interactive SHell),它们提供了更多的功能和用户友好的特性。通过Shell,用户可以执行操作系统提供的各种命令和工具,管理文件和目录,启动和管理进程,以及编写和执行Shell脚本。Shell脚本是由一系列Shell命令和控制结构组成的文本文件,它们可以批量执行一系列操作,自动化任务和流程。

        Shell具有强大的灵活性和可编程性,它允许用户根据自己的需求编写自定义的Shell脚本,以便更高效地完成特定的任务。Shell脚本也常用于系统管理、自动化部署、数据处理和软件开发等领域。

        如图1.1所示,Shell是计算机使用者与UNIX/Linux系统内核进行交流的渠。内核是操作系统的核心,涵盖资源管理、进程调度等众多内容。内核在开机的时候由bootloader引导时载入内存,关机之前一直由它管理计算机资源。它的主要功能是进程管理、物理资源管理、文件处理和计算机间及进程通信等。内核时中运行在内存上,其余的应用程序保存在磁盘上。当用户打开这些应用时,应用程序由内核加载到内存中运行。当用户关闭应用时,便相当于对程序发送信号,内核关闭应用并且进行资源回收。Shell伴随着用户的登录而启动,用户在终端输入命令,随后便可以解释执行。

图1.1 Linux与Shell的关系

2. 概要设计

        该系统的主要功能是在交互方式下解释从命令行输入的命令,Shell解析命令行,将其分解为词,词之间由空白分隔,空白由空格键组成。Shell处理文件I/O和后台进程,对命令行的处理结束后,Shell搜索命令并开始执行相应命令。

2.1 系统设计

        该应用程序系统主要分为终端显示(ShowTerminal)、命令解析(PhraseCommand)、日志记录(Log)、命令执行(ExecCommand)四个部分,通过循环执行这四个部分来实现MiniShell功能。

        Mini Shell系统具体指令功能的实现流程如下:

        a. 对于用户启动Shell后,在终端输入命令,首先是获取命令
        b. 对输入的字符串命令进行标准化处理,然后解析
        c. 通过相应函数执行输入的命令
        d. 循 环执行上述过程

        系统执行的流程图如下所示:

2.2 模块介绍

  • 终端显示

        实现终端显示功能,用户可通过显示界面进行查看程序输出的结果。

  • 解析命令

        对用户输入的命令进行解析,将其转换为计算机可以识别并执行的命令。

  • 日志记录

        对用户输入的命令进行记录,便于后续查看。

  • 执行相应命令

        执行解析后的命令,使其执行相应的功能。

3. 相关模块介绍

Main.c(主函数)

        主要用于调用其他函数中中的函数接口,实现MiniShell。

Terminal.c(终端显示及指令相关操作)

        用于实现终端的打印以及用户命令的接收,将命令解析后通过该Command.c中定义的相应函数来执行操作,目前可实现的指令如下:

        Linux ls(英文全拼: list directory contents)命令用于显示指定工作目录下之内容(列出目前工作目录所含的文件及子目录)。

        Linux touch命令用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。

        Linux rm(英文全拼:remove)命令用于删除一个文件或者目录。

        Linux mkdir(英文全拼:make directory)命令用于创建目录。

        Linux rmdir(英文全拼:remove directory)命令删除空的目录。

        Linux cd(英文全拼:change directory)命令用于改变当前工作目录的命令,切换到指定的路径。若目录名称省略,则变换至使用者的 home 目录 (也就是刚 login 时所在的目录)。另外,~ 也表示为 home 目录 的意思, . 则是表示目前所在的目录, .. 则表示目前目录位置的上一层目录。

        Linux cp(英文全拼:copy file)命令主要用于复制文件或目录。

        Linux mv(英文全拼:move file)命令用来为文件或目录改名、或将文件或目录移入其它位置。

        Linux pwd(英文全拼:print work directory) 命令用于显示工作目录。执行 pwd 指令可立刻得知您目前所在的工作目录的绝对路径名称。

        cat(英文全拼:concatenate)命令用于连接文件并打印到标准输出设备上。

        Linux chmod(英文全拼:change mode)命令是控制用户对文件的权限的命令。Linux/Unix 的文件调用权限分为三级 : 文件所有者(Owner)、用户组(Group)、其它用户(Other Users)。

        Linux ln(英文全拼:link files)命令是一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接。当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link)它就可以,不必重复的占用磁盘空间。

Command.c(具体指令实现)

        实现具体的命令操作,提供相应函数接口供执行函数调用。

Log.c(日志记录)

        将用户输入的命令记录在Log.txt文本文件中,便于后续查看。

4. 相关代码

Main.c(主函数)

#include "Log.h"
#include "Terminal.h"

int main(int argc, char const *argv[])
{
    char CommandBuff[4096] = {0}; //存储输入的命令
    char *pCmdArray[10] = {NULL}; //存储解析后的命令
    int CurCnt = 0;               //存储解析后的命令个数

    LogInit("Log.txt"); //日志记录文件初始化

    /* minishell执行流程 */
    while (1)
    {
        ShowTerminal();                                     //打印终端显示界面
        GetTerminal(CommandBuff, sizeof(CommandBuff));      //获取用户输入的指令并存储
        LogWrite(CommandBuff);                              //将用户输入的指令记录
        CurCnt = ParseTermainl(CommandBuff, pCmdArray, 10); //解析用户输入的命令
        ExecCommand(pCmdArray, CurCnt);                     //根据输入的命令执行相应函数
    }

    LogDeInit(); //关闭日志文件流指针,结束记录
     
    return 0;
}

Terminal(终端显示及指令相关操作)

        Terminal.h:

#ifndef __TERMINAL_H__
#define __TERMINAL_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

/* shell命令类型 */
typedef struct command
{
    char cmdname[32];            //命令字符串
    int (*pcmdfun)(char **, int); //命令对应的函数入口
}command_t;

extern int ShowTerminal(void);
extern int GetTerminal(char *pCmdBuf, int MaxLen);
extern int ParseTermainl(char *pCmdBuf, char **ppCmdArray, int MaxLen);
extern int ExecCommand(char **ppCmdArray, int CurCnt);

#endif
#include "Terminal.h"
#include "Command.h"

char gUserName[32] = {"Guosheng"};  //定义登陆账号
char gOsName[256] = {"minishell"};  //定义登陆名称

command_t gCmdList[]={
    {"cd", MyCd},
    {"chmod", MyChmod},
    {"ln", MyLn},
    {"ls", MyLs},   
    {"touch", MyTouch},
    {"pwd", MyPwd}, 
    {"exit", MyExit},
    {"mv", MyMv},
    {"cat", MyCat},
    {"cp", MyCp},
    {"rm", MyRm},
    {"rmdir", MyRmDir},
    {"mkdir", MyMkdir},
};

/********************************************
 * 函数名:ShowTerminal
 * 功能:打印用户与系统交互界面
 * 参数:
 * 		缺省
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int ShowTerminal(void)
{
    char tmpbuff[4096] = {0}; //定义数组存放当前目录路径
    char *ptmp = NULL;

    getcwd(tmpbuff, sizeof(tmpbuff)); //获取当前目录路径

    ptmp = tmpbuff + sizeof(tmpbuff); //指针指向数组末尾

    /* 指针指向最后一个'/' */
    while (*ptmp != '/')
    {
        ptmp--;
    }
    ptmp--;

    /* 指针指向倒数第二个'/' */
    while (*ptmp != '/')
    {
        ptmp--;
    }

    /* 指针指向第二个目录名词首目录 */
    if (strcmp(tmpbuff, "/"))
    {
        ptmp++;
    }

    printf("\033[1;32m%s@%s\033[0m:\033[1;34m~/%s\033[0m$ ", 
            gUserName, gOsName, ptmp); //打印指令输入界面
    
    return 0;
}

/********************************************
 * 函数名:GetTerminal
 * 功能:获取用户输入的指令内容
 * 参数:
 * 		pCmdBuf:指向用户输入的指令数组的指针
 *      MaxLen:用户输入的指令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int GetTerminal(char *pCmdBuf, int MaxLen)
{
    fgets(pCmdBuf, MaxLen, stdin);       //向pCmdBuf指向的数组写入用户输入的指令
    pCmdBuf[strlen(pCmdBuf) - 1] = '\0'; //数组末尾加上'\0'

    return 0;
}

/********************************************
 * 函数名:ParseTerminal
 * 功能:解析用户输入的指令
 * 参数:
 * 		pCmdBuf:指向存储用户输入的指令数组的指针
 *      ppCmdArray:指向解析后的指令数组的指针
 *      MaxLen:用户输入的指令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int ParseTermainl(char *pCmdBuf, char **ppCmdArray, int MaxLen)
{
    int cnt = 0;

    ppCmdArray[cnt] = strtok(pCmdBuf, " "); //解析用户输入的指令

    /* 未接收命令返回cnt(0) */
    if (NULL == ppCmdArray[cnt])
    {
        return cnt;
    }
    cnt++;

    /* 将分段后的命令依次放入指针数组 */
    while ((ppCmdArray[cnt] = strtok(NULL, " ")) != NULL)
    {
        cnt++;
    }
    
    return cnt;
}

/********************************************
 * 函数名:ExecCommand
 * 功能:执行用户输入的命令
 * 参数:
 *      ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:用户输入的指令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int ExecCommand(char **ppCmdArray, int CurCnt)
{
	int CurCmdLen = sizeof(gCmdList) / sizeof(gCmdList[0]); //定义Shell命令长度
	int i = 0;
	int ret = 0;

    /* 判断输入的命令并执行相关函数 */
	for (i = 0; i < CurCmdLen; i++)
	{
		if (!strcmp(ppCmdArray[0], gCmdList[i].cmdname))
		{
			ret = gCmdList[i].pcmdfun(ppCmdArray, CurCnt);
		}
        else
        {
            printf("%s: 未找到命令\n", ppCmdArray[0]);
            return ret;
        }
	}

	return ret;
}

Command(具体指令实现)

       Command.h:

#ifndef __COMMAND_H__
#define __COMMAND_H__

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

extern int MyCd(char **ppCmdArray, int CurCnt);
extern int MyChmod(char **ppCmdArray, int CurCnt);
extern int MyLn(char **ppCmdArray, int CurCnt);
extern int MyMv(char **ppCmdArray, int CurCnt); 
extern int MyLn(char **ppCmdArray, int CurCnt);  
extern int MyLs(char **ppCmdArray, int CurCnt);
extern int MyTouch(char **ppCmdArray, int CurCnt); 
extern int MyPwd(char **ppCmdArray, int CurCnt);
extern void MyExit(void);     
extern int MyCat(char **ppCmdArray, int CurCnt);
extern int MyCp(char **ppCmdArray, int CurCnt);
extern int MyRm(char **ppCmdArray, int CurCnt);
extern int MyRmDir(char **ppCmdArray, int CurCnt);  
extern int MyMkdir(char **ppCmdArray, int CurCnt);
extern void StatFile(char *pfilename);

#endif
#include "Command.h"

/********************************************
 * 函数名:MyCd
 * 功能:切换当前工作目录 
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyCd(char **ppCmdArray, int CurCnt)
{   
    /* 判断用户输入命令模式是否正确 */
    if (2 != CurCnt)
	{
		fprintf(stderr, "Usage:cd mode filename\n");
		return -1;
	}

	/* 切换工作目录 */
    if (ppCmdArray[1] != NULL)
    {
        chdir(ppCmdArray[1]);
    }
    
    return 0;
}

/********************************************
 * 函数名:MyChmod
 * 功能:切换当前工作目录 
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyChmod(char **ppCmdArray, int CurCnt)
{
    mode_t mode;
    char *p = NULL;

    /* 判断用户输入命令模式是否正确 */
    if (3 != CurCnt)
	{
		fprintf(stderr, "Usage:chmod mode filename\n");
		return -1;
	}
	
	
    p = ppCmdArray[1];
	while (*p != '\0')
	{
		mode = mode * 8 + *p - '0';	//将转换为8进制数字
		p++;
	}

    chmod(ppCmdArray[2], mode);		//修改文件权限


	return 0;
}

/********************************************
 * 函数名:MyLn
 * 功能:为某一个文件在另外一个位置建立一个同步的链接
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyLn(char **ppCmdArray, int CurCnt)
{
	if (CurCnt < 3)
	{
		fprintf(stderr, "Usage:ln [-s] src.txt link.txt\n");
		return -1;
	}

	if (3 == CurCnt)
	{
		link(ppCmdArray[1], ppCmdArray[2]);
	}
	else if (4 == CurCnt && !strcmp(ppCmdArray[1], "-s"))
	{
		symlink(ppCmdArray[2], ppCmdArray[3]);
	}

	return 0;
}

/********************************************
 * 函数名:MyMv
 * 功能:命令用来为文件或目录改名,
 * 	   或将文件或目录移入其它位置
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyMv(char **ppCmdArray, int CurCnt)
{
	int ret = 0;
	struct stat buf;
	char newpath[1024] = {0};

	/* 判断用户输入命令模式是否正确 */
	if (CurCnt != 3)
	{
		fprintf(stderr, "Usage:mv srcfile dstpath\n");
		return -1;
	}

	ret = stat(ppCmdArray[2], &buf);
	if (0 == ret && S_ISDIR(buf.st_mode))
	{
		sprintf(newpath, "%s/%s", ppCmdArray[2], ppCmdArray[1]);
	}
	else
	{
		sprintf(newpath, "%s", ppCmdArray[2]);
	}

	ret = rename(ppCmdArray[1], newpath);
	if (-1 == ret)
	{
		perror("fail to rename");
		return -1;
	}
	
	return 0;
}

/********************************************
 * 函数名:MyLs
 * 功能:打印出当前目录的列表
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyLs(char **ppCmdArray, int CurCnt)
{
	char tmpbuff[4096] = {0};	//存放当前目录路径
	DIR *dp = NULL;				//定义DIR结构体的指针dp
	struct dirent *pp = NULL;	//pp指向结构dirent

	/* 判断用户输入命令模式是否正确 */
	if (CurCnt >= 3)
	{
		fprintf(stderr, "Usage:ls mode filename\n");
		return -1;
	}
	/* 执行ls */
	else if (CurCnt == 1)
	{
		getcwd(tmpbuff, sizeof(tmpbuff));	//获取当前目录路径存放到tmpbuff数组

		dp = opendir(tmpbuff);				//打开tmpbuff指向的目录,并返回*DIR结构的目录流
    	/* 如果打开制定的目录失败则打印出错信息 */
		if (NULL == dp)
    	{
			perror("fail to opendir");
			return -1;
		}

		/* 遍历当前目录下的文件并打印 */
		while ((pp = readdir(dp)) != NULL)
		{
			if ('.' == pp->d_name[0])
			{
				continue;
			}	

			printf("%s  ", pp->d_name);
		}

		printf("\n");	
		closedir(dp);
	}
	/* 执行ls -a */
	else if(CurCnt == 2 && !(strcmp(ppCmdArray[1], "-a")))
	{
		getcwd(tmpbuff, sizeof(tmpbuff));	//获取当前目录路径存放到tmpbuff数组

		dp = opendir(tmpbuff);				//打开tmpbuff指向的目录,并返回*DIR结构的目录流
    	/* 如果打开制定的目录失败则打印出错信息 */
		if (NULL == dp)
    	{
			perror("fail to opendir");
			return -1;
		}

		/* 遍历当前目录下的文件并打印 */
		while ((pp = readdir(dp)) != NULL)
		{
			printf("%s  ", pp->d_name);
		}

		printf("\n");	
		closedir(dp);
	}
	/* 执行ls -l */
	else if(CurCnt == 2 && !(strcmp(ppCmdArray[1], "-l")))
	{
		dp = opendir(".");	//打开当前目录
		if (NULL == dp)
		{
			perror("fail to open");
			return -1;
		}

		/* 获取当前目录下的文件并将其名称存放在数组中 */
		while (NULL != (pp = readdir(dp)))
		{
			if ('.' == pp->d_name[0])
			{
				continue;
			}
			sprintf(tmpbuff, "%s", pp->d_name);
			StatFile(tmpbuff);

			close(dp);
		}
	}

	return 0;
}

/********************************************
 * 函数名:StatFile
 * 功能:获取文件信息
 * 参数:
 * 		pfilename:指向存储文件名称的数组
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
void StatFile(char *pfilename)
{         
	struct stat finformation;	//存放文件的信息
	int ret = 0;				//接收返回值

	ret = lstat(pfilename,&finformation);	//获取信息给finformation
	if(-1 == ret)							//判断是否获取成功
	{
		perror("fail to lstat");
	}

	switch(finformation.st_mode & __S_IFMT) //文件类型
	{
			case __S_IFBLK: putchar('b'); break;
			case __S_IFCHR: putchar('c'); break;
			case __S_IFDIR: putchar('d'); break;
			case __S_IFREG: putchar('-'); break;
			case __S_IFLNK: putchar('l'); break;
			case __S_IFSOCK: putchar('s');break;
			case __S_IFIFO: putchar('p'); break;
	}

	finformation.st_mode & S_IRUSR ? putchar('r') : putchar('-');	//创建者权限
	finformation.st_mode & S_IWUSR ? putchar('w') : putchar('-');
	finformation.st_mode & S_IXUSR ? putchar('x') : putchar('-');

	finformation.st_mode & S_IRGRP ? putchar('r') : putchar('-');	//同组人员权限
	finformation.st_mode & S_IWGRP ? putchar('w') : putchar('-');
	finformation.st_mode & S_IXGRP ? putchar('x') : putchar('-');

	finformation.st_mode & S_IROTH ? putchar('r') : putchar('-');	//其余人权限
	finformation.st_mode & S_IWOTH ? putchar('w') : putchar('-');
	finformation.st_mode & S_IXOTH ? putchar('x') : putchar('-');

	printf(" %ld",finformation.st_nlink);	//硬链接个数

	struct passwd *puid = NULL;				//uid名
	puid = getpwuid(finformation.st_uid);
	printf(" %s",puid->pw_name);

	struct group *pgid = NULL;				//gid名
	pgid = getgrgid(finformation.st_gid);
	printf(" %s",pgid->gr_name);

	printf(" %5ld",finformation.st_size);	//文件大小

	struct tm *ptime = NULL;				//最近编辑时间
	char *mon[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
	ptime = localtime(&finformation.st_ctime);
	printf(" %s%d  %d:%d %s\n",mon[ptime->tm_mon],ptime->tm_mday,ptime->tm_hour
													,ptime->tm_min,pfilename);
 
         
}

/********************************************
 * 函数名:MyCat
 * 功能:将文件内容打印在终端
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyCat(char **ppCmdArray, int CurCnt)
{
	int fd = 0;				//定义一个文件描述符
	char cont[10240] = {0};	//定义一个字符型数组存放读取到的文件内容

	/* 判断用户输入命令模式是否正确 */
	if (CurCnt != 2)
	{
		fprintf(stderr, "Usage:cat mode filename\n");
		return -1;
	}

	/* 打开文件并返回文件描述符 */
	fd = open(ppCmdArray[1], O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open(cat)");
		return -1;
	}

	read(fd, cont, sizeof(cont));	//读取文件内容存储到数组中
	printf("%s", cont);				//打印读取到的文件内容

	close(fd);						//关闭文件描述符

	return 0;
}

/********************************************
 * 函数名:MyCp
 * 功能:拷贝文件
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyCp(char **ppCmdArray, int CurCnt)
{
	int fsrc = 0;
	int fdst = 0;
	int len = 0;			//定义读取到的内容长度
	char str[4096] = {0};	//定义字符型数组来存储文件内容

	/* 打开源文件 */
	fsrc = open(ppCmdArray[1], O_RDONLY);
	if (-1 == fsrc)
	{
		perror("fail to open(cp)");
		return -1;
	}

	/* 打开目的文件 */
	fdst = open(ppCmdArray[2], O_WRONLY | O_TRUNC |O_CREAT, 0664);	//110 110 100
	if (-1 == fdst)
	{
		perror("fail to open(cp)");
		return -1;
	}

    while(( len = read(fsrc, str, sizeof(str)) ) > 0 ) 
	{
        write(fdst, str, len);	//将读取到的文件内容写入到目标文件中
    }

	close(fsrc);
	close(fdst);

	return 0;
}

/********************************************
 * 函数名:MyMv
 * 功能:建立一个新的文件
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyTouch(char **ppCmdArray, int CurCnt)
{
	char filename[1024] = {0};	//文件名称
	char filepath[1024] = {0};	//当前文件路径
	FILE *fp = NULL;

	getcwd(filepath, sizeof(filepath));	//获取当前目录的路径并存储到数组中
	
	sprintf(filename, "%s/%s", filepath, ppCmdArray[1]);
	
	fp = fopen(filename, "a");
	if (NULL == fp)
	{
		perror("fail to fopen(touch)");
		return -1;
	}

	fclose(fp);
	
	return 0;
}

/********************************************
 * 函数名:MyRm
 * 功能:删除文件
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyRm(char **ppCmdArray, int CurCnt)
{
	/* 判断用户输入命令模式是否正确 */
	if (CurCnt != 2)
	{
		fprintf(stderr, "Usage:rm mode filename\n");
		return -1;
	}

	remove(ppCmdArray[1]);	//删除指定文件

	return 0;
}

/********************************************
 * 函数名:MyRmDir
 * 功能:删除文件夹
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyRmDir(char **ppCmdArray, int CurCnt)
{
	/* 判断用户输入命令模式是否正确 */
	if (CurCnt != 2)
	{
		fprintf(stderr, "Usage:rmdir mode filename\n");
		return -1;
	}

	rmdir(ppCmdArray[1]);	//删除指定文件夹

	return 0;
}
/********************************************
 * 函数名:MyMkdir
 * 功能:创建文件夹
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyMkdir(char **ppCmdArray, int CurCnt)
{
	/* 判断用户输入命令模式是否正确 */
	if (CurCnt != 2)
	{
		fprintf(stderr, "Usage:mkdir mode filename\n");
		return -1;
	}

	mkdir(ppCmdArray[1], 0777);	//创建指定文件夹

	return 0;
}

/********************************************
 * 函数名:MyPwd
 * 功能:显示工作目录
 * 参数:
 * 		ppCmdArray:指向解析后的指令数组的指针
 *      CurCnt:解析后的命令个数
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int MyPwd(char **ppCmdArray, int CurCnt)
{
	char filepath[1024] = {0};
	
	getcwd(filepath, sizeof(filepath));
	puts(filepath);

	return 0;
}

/********************************************
 * 函数名:MyExit
 * 功能:退出minishell
 * 参数:
 * 		缺省
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
void MyExit(void)
{
	exit(0);
}

Log.c(日志记录)

#ifndef __LOG_H__
#define __LOG_H__

#include <stdio.h>
#include <time.h>//时间库函数:定义了四个变量类型、两个宏和各种操作日期和时间的函数

extern int LogInit(char *pFileName);
extern int LogWrite(char *pstr);
extern int LogDeInit(void);

#endif
#include "Log.h"

FILE *fp = NULL;//定义文件流指针

/********************************************
 * 函数名:LogInit
 * 功能:日志文件初始化
 * 参数:
 * 		pFileName:日志文件名称
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int LogInit(char *pFileName)
{
    fp = fopen(pFileName, "a");//以追加方式打开文件
    if (NULL == fp)
    {
        perror("fail to fopen");
        return -1;
    }

    return 0;
}

/********************************************
 * 函数名:LogWrite
 * 功能:日志文件初始化
 * 参数:
 * 		pstr:输入命令名称
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int LogWrite(char *pstr)
{
	time_t t;
	struct tm *ptm = NULL;

	time(&t);
	ptm = localtime(&t);
	fprintf(fp, "[%04d-%02d-%02d %02d:%02d:%02d]:%s\n", ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday, 
														ptm->tm_hour, ptm->tm_min, ptm->tm_sec, pstr);
	fflush(fp);//刷新流fp文件流的输出缓冲区。

	return 0;
}

/********************************************
 * 函数名:LogDeInit
 * 功能:关闭文件流指针
 * 参数:
 * 		缺省
 * 返回值:
 * 		成功返回0 
 * 		失败返回-1 
 * 注意事项:
 * 		无
 *******************************************/
int LogDeInit(void)
{
	fclose(fp);

	return 0;
}

Makefile

        对所有源文件进行编译后生成minishell可执行文件。

OBJ := minishell
OBJS += Main.c
OBJS += Terminal.c
OBJS += Log.c
OBJS += Command.c

$(OBJ):$(OBJS)
	gcc $^ -o $@

.PHONY:
clean:
	rm $(OBJ)

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值