目录
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伴随着用户的登录而启动,用户在终端输入命令,随后便可以解释执行。
![](https://img-blog.csdnimg.cn/img_convert/22f5d8488fe127df61e4609257d031eb.png)
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)