一、项目说明
利用Linux中IO接口实现 terminal 的shell命令 制作成一个minishell 的可执行文件
输出形式为:
[linux@Ubuntu:当前所在目录]
项目要求:
一、基本实现
- cd
- ls
- ls -l
- ls -a
- pwd
- mkdir
- mv
- rm
- rmdir
- cp
- touch
- cat
- ln
这13个shell 命令
二、将用户输入命令的时间及其命令写入到日志文件中并纪录
三、根据代码功能,将代码划分成若干个文件,并使用多文件编程来实现代码
二、实现思路
1.打印终端命令行如何实现?
[linux@Ubuntu:当前所在目录] getcwd -> 绝对路径 -> 显示最后一部分内容
/home/linux/Desktop/dirname
2.能够接收用户的命令?
ls fgets -> 解析字符串获得命令及其参数
touch a.txt
rmdir dirname
cp a.txt b.txt
* 难点在于如何解析输入进去的字符串
后面有三种方法:分别是 1.切割字符串存入新的字符串中
2.使用指针
3.使用strtok函数
将获得的一段段字符串分别进行传参。
3.实现对应命令?
ls opendir readdir closedir
ls -a opendir readdir closedir
ls -l
touch fopen fclose
rm remove
mkdir mkdir
rmdir rmdir
cp IO
cat IO
ln symlink link
pwd getcwd
cd chdir
mv rename
chmod chmod
4.命令记录
写入文件 fopen fprintf fclose
三、具体实现
分为
main.c
command.c
command.h
terminal.c
terminal.h
record.c
record.h
record.txt
makfile (进行链接)
首先是 main.c 其中定义了 接收命令的数组
#include<stdio.h>
#include<string.h>
#include"terminal.h"
#include"command.h"
#include"record.h"
int main(void)
{
char command[1024] = {0};
char *parg[10] = {NULL};
int curcmdlen = 0;
InitRecord();
while(1)
{
ShowTerminal();
GetCommand(command,1024);
if(!strcmp(command,"exit"))
{
break;
}
curcmdlen = SplitCommand(command,parg,10);
ExecCommand(parg,curcmdlen);
}
return 0;
}
command.c
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
int Myls(void)
{
DIR *dp = NULL;
struct dirent *pp =NULL;
dp = opendir(".");
if(NULL == dp)
{
perror("fail to opendir");
return -1;
}
while(1)
{
pp = readdir(dp);
if(NULL == pp)
{
break;
}
if('.' == pp->d_name[0])
{
continue;
}
printf("%s ",pp->d_name);
}
putchar('\n');
closedir(dp);
return 0;
}
int Myls_a(void)
{
DIR *dp = NULL;
struct dirent *pp =NULL;
dp = opendir(".");
if(NULL == dp)
{
perror("fail to opendir");
return -1;
}
while(1)
{
pp = readdir(dp);
if(NULL == pp)
{
break;
}
printf("%s ",pp->d_name);
}
putchar('\n');
closedir(dp);
return 0;
}
int DisplayFile(char *pfilename)
{
struct stat buf;
int ret = 0;
struct passwd *pwd =NULL;
struct group *grp = NULL;
struct tm *ptm =NULL;
char tmpbuff[1024] = {0};
char *mon[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
ret = lstat(pfilename,&buf);
if(-1 == ret)
{
perror("fail to lstat");
return -1;
}
if(S_ISBLK(buf.st_mode))
{
putchar('b');
}
else if(S_ISCHR(buf.st_mode))
{
putchar('c');
}
else if(S_ISDIR(buf.st_mode))
{
putchar('d');
}
else if(S_ISREG(buf.st_mode))
{
putchar('-');
}
else if(S_ISLNK(buf.st_mode))
{
putchar('l');
}
else if(S_ISSOCK(buf.st_mode))
{
putchar('s');
}
else if(S_ISFIFO(buf.st_mode))
{
putchar('p');
}
buf.st_mode & S_IRUSR ?putchar('r'):putchar('-');
buf.st_mode & S_IWUSR ?putchar('w'):putchar('-');
buf.st_mode & S_IXUSR ?putchar('x'):putchar('-');
buf.st_mode & S_IRGRP ?putchar('r'):putchar('-');
buf.st_mode & S_IWGRP ?putchar('w'):putchar('-');
buf.st_mode & S_IXGRP ?putchar('x'):putchar('-');
buf.st_mode & S_IROTH ?putchar('r'):putchar('-');
buf.st_mode & S_IWOTH ?putchar('w'):putchar('-');
buf.st_mode & S_IXOTH ?putchar('x'):putchar('-');
printf("%ld",buf.st_nlink);
pwd = getpwuid(buf.st_uid);
printf(" %s",pwd->pw_name);
grp = getgrgid(buf.st_gid);
printf(" %s",grp->gr_name);
printf("%5ld",buf.st_size);
ptm = localtime(&buf.st_mtime);
printf("%s %02d %02d:%02d",mon[ptm->tm_mon],ptm->tm_mday,ptm->tm_hour,ptm->tm_min);
printf(" %s",pfilename);
if(S_ISLNK(buf.st_mode))
{
readlink(pfilename,tmpbuff,sizeof(tmpbuff));
printf("->%s",tmpbuff);
}
printf("\n");
return 0;
}
int Myls_l(void)
{
DIR *dp = NULL;
struct dirent*pp = NULL;
dp = opendir(".");
if(NULL == dp)
{
perror("fial to opendir");
return -1;
}
while(1)
{
pp = readdir(dp);
if(NULL == pp)
{
break;
}
if('.' == pp->d_name[0])
{
continue;
}
DisplayFile(pp->d_name);
}
closedir(dp);
return 0;
}
int Touch(const char *filename)
{
FILE *fp = NULL;
fp = fopen(filename,"w");
if(NULL == fp)
{
perror("fail fopen");
return -1;
}
fclose(fp);
return 0;
}
int Cp(const char *file1,const char *file2)
{
FILE *fsrc = NULL;
FILE *fdst = NULL;
size_t nret = 0;
char tmpbuff[4096] = {0};
fsrc = fopen(file1,"r");
if(NULL ==fsrc)
{
perror("fail to fopen");
return -1;
}
fdst = fopen(file2,"w");
if(NULL == fdst)
{
perror("fail to open");
return -1;
}
while(1)
{
nret =fread(tmpbuff,1,sizeof(tmpbuff),fsrc);
if(0 == nret)
{
break;
}
fwrite(tmpbuff,1,nret,fdst);
}
fclose(fsrc);
fclose(fdst);
return 0;
}
int Cat(char *pcmdbuf)
{
FILE *fp = NULL;
size_t ret = 0;
int i = 0;
char tmpbuff[4096] = "0";
fp = fopen(pcmdbuf,"r");
if(NULL == fp)
{
perror("fail to open");
return -1;
}
ret=fread(tmpbuff,1,sizeof(tmpbuff),fp);
for(i = 0;i<ret;++i)
{
printf("%c",tmpbuff[i]);
}
fclose(fp);
return 0;
}
int Pwd(char* pcmdbuf)
{
char tmpbuff[4096] ={0};
getcwd(tmpbuff,sizeof(tmpbuff));
printf("%s\n",tmpbuff);
return 0;
}
int Ln(char *file1,char *file2)
{
link(file1,file2);
return 0;
}
int Mychmod(char *pcmdbuf,char *mode)
{
int ret = 0;
int len = strlen(mode);
int num = 0;
int i,j;
int a[len];
for(i = 0;i< len ;++i,++mode)
{
a[i] = *mode -48;
}
for(i = len -1,j = 1;i>=0;--i,j*=10)
{
num += a[i]*j;
}
chmod(pcmdbuf,num);
return 0;
}
command.h
#ifndef _COMMAND_H
#define _COMMAND_H
extern int Myls(void);
extern int Myls_a(void);
extern int DisplayFile(char *pfilename);
extern int Myls_l(void);
extern int Touch(const char *filename);
extern int Cp(const char *file1,const char *file2);
extern int Cat(char *pcmdbuf);
extern int Pwd(char* pcmdbuf);
extern int Ln(char *file1,char *file2);
extern int Mychmod(char *pcmdbuf,char *mode);
#endif
terminal.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include"command.h"
/*********************************
* File Name:ShowTerminal
* Parameter: void
* Return value : succes >0
* fail >-1
********************************/
int ShowTerminal(void)
{
char tmpbuff[1024] = {0};
char *ptmp = NULL;
getcwd(tmpbuff,sizeof(tmpbuff));
ptmp = tmpbuff + strlen(tmpbuff); // 首地址+字符串长度 尾 ‘/0’ 的地址
while(*ptmp != '/')
{
ptmp--;
}
if(strcmp(tmpbuff,"/"))
{
ptmp++;
}
printf("[linux@Ubuntu:%s]",ptmp);
return 0;
}
/*********************************
* File Name:GetCommand
* Parameter:
* pcmdbuff:存放命令空间首地址
* maxlen:最大存放字符串个数
* Return value : succes >0
* fail >-1
********************************/
int GetCommand(char *pcmdbuff,int maxlen)
{
fgets(pcmdbuff,maxlen,stdin);
pcmdbuff[strlen(pcmdbuff)-1] = '\0';
return 0;
}
/*********************************
* File Name:SplitCommand 解析命令 把命令分成小段 返回小段的个数
* Parameter:
* pcmdbuff:存放命令空间首地址
* maxlen:最大存放字符串个数
* parg:存放解析后字符串地址的指针数组
* Return value : succes >解析到命令的个数
* fail >-1
********************************/
int SplitCommand(char *pcmdbuff,char **parg,int maxlen) // char **parg == char *parg[]
{
char *ptmp = NULL;
int cnt = 0;
ptmp = pcmdbuff;
while(1)
{
parg[cnt] = ptmp;
cnt++;
while(*ptmp != '\0' && *ptmp != ' ')//指针在遇到‘ ’或最后的‘\0’时停下
{
ptmp++;
}
if('\0'== *ptmp)
{
break;
}
*ptmp = '\0';//给每个命令段后面补上 ‘\0’使其成为一个字符串
ptmp++;
while(*ptmp == ' ')
{
ptmp++;
}
}
return cnt;
}
/*********************************
* File Name:ExecCommand 判断命令
* Parameter:
* curlen:命令的个数
* parg:存放解析后字符串地址的指针数组
* Return value : succes >0
* fail >-1
********************************/
int ExecCommand(char **parg ,int curlen)
{
if(!strcmp(parg[0],"ls"))
{
if(2 == curlen && !strcmp(parg[1],"-l"))
{
Myls_l();
return 0;
}
else if(2 == curlen && !strcmp(parg[1],"-a"))
{
Myls_a();
return 0;
}
Myls();
}
else if(!strcmp(parg[0],"cp"))
{
Cp(parg[1],parg[2]);
}
else if(!strcmp(parg[0],"cat"))
{
Cat(parg[1]);
}
else if(!strcmp(parg[0],"pwd"))
{
Pwd(parg[1]);
}
else if(!strcmp(parg[0],"ln"))
{
Ln(parg[1],parg[2]);
}
else if(!strcmp(parg[0],"cd"))
{
chdir(parg[1]);
}
else if(!strcmp(parg[0],"touch"))
{
Touch(parg[1]);
}
else if(!strcmp(parg[0],"mkdir"))
{
mkdir(parg[1],0777);
}
else if(!strcmp(parg[0],"rmdir"))
{
rmdir(parg[1]);
}
else if(!strcmp(parg[0],"rm"))
{
remove(parg[1]);
}
else if(!strcmp(parg[0],"ln"))
{
Ln(parg[1],parg[2]);
}
else if(!strcmp(parg[0],"chmod"))
{
Mychmod(parg[1],parg[2]);
}
return 0;
}
terminal.h
#ifndef _TERMINAL_H
#define _TERMINAL_H
extern int ShowTerminal(void);
extern int ExecCommand(char **parg ,int curlen);
extern int SplitCommand(char *pcmdbuff,char **parg,int maxlen);
extern int GetCommand(char *pcmdbuff,int maxlen);
#endif
record.c
#include"record.h"
#include<stdio.h>
#include<time.h>
FILE *fp = NULL;
int InitRecord(void)
{
fp = fopen(RECORD_PATH,"a");
if(NULL == fp)
{
perror("fail fopen");
return -1;
}
return 0;
}
int RecordCommand(char *pcmdbuf)
{
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,pcmdbuf);
fflush(fp);//更新缓存 让数据存入文件中;
return 0;
}
int DelnitRecord(void)
{
if(fp != NULL)
{
fclose(fp);
fp = NULL ;
}
return 0;
}
record.h
#ifndef _RECORD_H
#define _RECORD_H
#define RECORD_PATH "./record.txt"
extern int InitRecord(void);
extern int RecordCommand(char *pcmdbuf);
extern int DelnitRecord(void);
#endif
makefile
OBJ = minishell
OBJS += main.c
OBJS += terminal.c
OBJS += command.c
OBJS += record.c
$(OBJ):$(OBJS)
gcc $^ -o $@
.PHONY:
clean:
rm$(OBJ)
install:
sudo cp $(OBJ) /bin
uninstall:
sudo rm /bin/$(OBJ)
四、实现效果
五、项目总结
1.Terminal多窗口的使用可以更快捷方便的进行代码的调试
以及vim 编辑器 的 vsp功能 分屏打开文件 利于 复制y 粘贴p
2.之前所学IO的多有不熟悉 ,需要翻阅笔记才能对照着打出来。
一是不熟练,其次就是理解也不够深刻
3.对 man手册的使用更为熟练了 ,主要是对各个函数的参数进一步了解
4.对char *tmpbuf 与 char tmpbuf[4096] ={0};
以及加强char **p 和 char *p[]的理解
*************************************
Makefile
Makefile 是一种文本文件,通常用于描述软件项目的构建规则和依赖关系。它配合构建工具如 GNU Make,可以自动化构建过程,提高软件开发的效率。
strtok函数
char *strtok(char *str, const char *delim);
功能: 用于将字符串分割成多个子字符串
参数:
str
: 要分割的字符串,第一次调用时传入需要分割的字符串,后续调用传入 NULL 即可。delim
: 分割字符串时所用的分隔符,可以是单个字符或字符串。
返回值:一个指向分割得到的子字符串的指针
strtok
函数在第一次调用时,会使用参数 str
指向的字符串进行分割操作,并返回第一个找到的子字符串。在后续调用中,如果传入 NULL
作为参数,strtok
函数会继续使用上一次的字符串进行分割并返回下一个子字符串,直到字符串被完全分割。在分割过程中,strtok
函数会修改参数 str
指向的字符串,在其中插入 NULL 终止符以分隔子字符串。
需要注意的是,由于 strtok
函数会修改传入的字符串,因此在多线程环境下需要谨慎使用。
#include <string.h>
#include <stdio.h>
int main(void)
{
char cmdbuf[1024] = {0};
char *pret = NULL;
char *parg[10] = {NULL};
int cnt = 0;
//ln -s file.txt a.txt
gets(cmdbuf);
parg[cnt] = strtok(cmdbuf, " ");
cnt++;
while (1)
{
parg[cnt] = strtok(NULL, " ");
if (NULL == parg[cnt])
{
break;
}
cnt++;
}
int i = 0;
for (i = 0; i < cnt; i++)
{
printf("parg[%d] = %s\n", i, parg[i]);
}
return 0;
}