c语言实现mybash
关于bash
bash是属于shell程序的一种,即解释命令器的一种。shell不是具体哪一款程序,是一类程序的统称,这些程序只要是能够按照用户的要求去调用操作系统的接口,就可以称之为shell程序。
通过shell可以将用户与内核联系起来。
我们的bash是怎么实现的
想一想该怎么实现
首先我们先从键盘获取我们需要执行的命令,包括后面的参数,例如:ls -f。然后通过分析键盘获得的数据来进行操作,若输入是合法的命令以及参数,我们fork复制一个进程,通过exce替换/use/bin的进程实现我们命令。
假如我们已经执行了我们的mbash,这时候我们需要通过键盘获取执行命令,怎么做呢?
需要注意的是,如果我们输入“ls”,buff获取到的实际上是“ls\n”,继而我们需要将“\n”给他清楚掉。
这里我们使用一个get_cmd函数来将我们buff中来自键盘的数据进行拆分。(ARGV来自宏定义为:10)
这里使用的是strtok字符串函数,在buff中若是遇到 " " 空格就将字符串拆分,存入到s中。随后再次使用strtok时候第一个参数直接传入NULL,因为strlen函数中内部有一个全局变量来记录上一次切割的位置。
输入结束后呢,就要进行判断我们输入的命令是什么。如果是NULL就继续循环(我们的程序在一个while(1)中运行),如果是exit就退出,如果是cd就使用chdir函数进行当前目录的切换,并且继续循环。
随后就是复制一个进程,来对我们从键盘获取的命令进行操作,使用excvp的原因是与excel相比,它可以传入一个数组,继而可以传入多个参数。然后就将子进程切换到我们需要的那个操作了。
但是好像又不太对
首先我们加上一个宏定义
至于这个宏定义的作用,我们后面再提
我们上面操作实现的mybash,方法的实现依然是来自 " /usr/bin "下的程序我们只是通过方法调用了这些程序,而并不是来自我们自己实现。下面就要讲一下怎么真正实现自己的mybash
首先我们改一下bash前面的信息。
使用这样一个函数。不仅可以随时更改并显示当前目录,而且有了颜色,更像一个真正的bash。
这里的代码几乎与前面相同没有太多的修改。
然后就是对复制进程代码的修改,这里我们进行了一个判断,如果我们输入的命令开头是" ./ 或者 / ",我们认定需要执行的命令不是一个系统命令,而是我们自己完成的可执行程序。便把这个命令存入path中。
若它是一个系统命令(执行没有添加所在目录,即没有" ./ 或者 / "),那么我们就去我们自己mybin中寻找这个执行文件,这时候前面提到的PATH宏定义就出来了。这里存放的目录就是存放个人实现执行命令的目录,当我们输入,例:ls 我们就会在该目录下寻找这个可执行文件,并且执行。
在这里,我只实现了个别命令,随后会在末尾贴上代码实现代码。
这次又使用execv来替换进程,这个首先传入一个目录,并在目录下寻找需要替换掉的进程。
这样只要我们不断完善自己的mybin就可以实现一个完善的使用的属于自己的命令解释器。
所有的代码
mybash
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<pwd.h>
#define ARGC 10
#define PATH "/home/zyq/Linux/c208/mybash/mybin/"
void printf_info()
{
char *s = "$";
int uid = getuid();
if(uid == 0)
{
s="#";
}
struct passwd *p = getpwuid(uid);
if(p == NULL)
{
printf("$");
fflush(stdout);
return;
}
char hostname[128] = {0};
gethostname(hostname,128);
char curr_dir[256] = {0};
getcwd(curr_dir,256);
printf("\e[1;32m%s@%s\e[0m \e[1;34m%s\e[0m%s",p->pw_name,hostname,curr_dir,s);
char *get_cmd(char* buff,char* myargv[])
{
if(buff == NULL|| myargv == NULL)
{
return NULL;
}
int i =0;
char * s = strtok(buff," ");
while(s!=NULL)
{
myargv[i++] = s;
s = strtok(NULL," ");
}
return myargv[0];
}
int main()
{
while(1)
{
char path[256] = {0};
//封装实现 头
//printf("zyq@localhost:%s ~$ ",getcwd(path,256));
//fflush(stdout);
printf_info();//封装 zyq@localhost: -$
char buff[128] = {0};
fgets(buff,128,stdin); //"ls\n"
buff[strlen(buff)-1] = 0;//去除\n
char* argv[ARGC] = {0};
char* cmd = get_cmd(buff,argv); //得到参数的数组
if(cmd == NULL)
{
continue;
}
else if(strcmp(cmd,"exit")==0)
{
break;
}
else if(strcmp(cmd,"cd")==0)
{
if(argv[1]!=NULL)
{
if(chdir(argv[1])!=0)
{
perror("cd error");
}
}
continue;
}
pid_t pid = fork();
if(pid == -1)
{
continue;
}
if(pid == 0)
{
//execv 参数: 路径 名称(argv 参数)
//execvp(cmd,argv); //多个参数
char path[256] = {0};
if(strncmp(cmd,"./",2) == 0 || strncmp(cmd,"/",1) == 0)
{
strcpy(path,cmd);
}
else
{
strcpy(path,PATH);//把我们提前写好程序的目录拷贝如path
strcat(path,cmd);//在path后面添加上目录下的命令名字
}
execv(path,argv);
printf("exec error");
exit(0);
}
wait(NULL);
}
}
个人实现的一些mybin代码
clear
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
printf("\033{2J\033{0:0H");
}
ls
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<dirent.h>
#include<sys/types.h>
#include<sys/stat.h>
//opendir 打开目录流
//readdir
//closedir
int main()
{
char path[256] = {0};
getcwd(path,256);//获取当前位置
//打开目录流
DIR * p = opendir(path);
if(p == NULL)
{
return 0;
}
//读目录流
struct dirent * s = NULL;
while((s = readdir(p)) != NULL )
{
if(strncmp(s->d_name,".",1) == 0)
{
continue;
}
struct stat st;
stat(s->d_name,&st);
if(S_ISDIR(st.st_mode))//判断是不是目录
{
printf("\033[1;34m%s \033[0m",s->d_name);
}
else
{
if(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
{
printf("\033[1;32m%s \033[0m",s->d_name);
}
else
{
printf("%s ",s->d_name);
}
}
}
//关闭目录流
printf("\n");
closedir(p);
}
这里使用的了DIR函数,来实现ls的一系列操作,并且对颜色进行了调整,更加真实且实用。
pwd
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
char path[256] = {0};
getcwd(path,256);
printf("%s\n",path);
}