Linux中shell命令解释器的实现

本文详细解析了Shell命令解释器的工作原理,介绍了内置命令与普通命令的分类,讨论了如何通过`fork()`和`exec()`处理命令,以及特定注意事项,如清理命令行尾部的换行符和子进程中的`exit(0)`的作用。
摘要由CSDN通过智能技术生成

(1)清楚命令解释器的含义:

在计算机科学中,Shell俗称壳,是指“为使用者提供操作界面”的软件(command interprter,命令解析器)。它接受用户命令,然后调用相应的应用程序

(2)清楚系统中命令的分类,分为:

内置命令:exit cd

普通命令(子进程帮助完成的):ls pwd ps cp

Shell脚本中命令运行时若不用创建子进程则是内置命令;—shell把它们当成脚本中的函数处理; 内置命令在目录列表时是看不见的,它们由Shell本身提供。

所以在bash命令解释器的实现中,处理内置命令和普通命令需要分开处理,普通命令用“fork()+exec(让子进程执行exec)”来实现,让子进程去处理

(3)主函数以及相关函数大致思路:

1>

while(1)//因为命令解释器不会自动退出,所以需要让它是死循环
{
printf("stu@quzijie:/usr/bin $");//模拟Linux系统下打印用户名相对路径
fgets(buff,128,stdin);//用fgets函数获取从键盘内输入的命令
//分割命令(这里使用strtok分割函数)
char *myargv[10]={0};//myargv是获取到的命令,用数组进行保存
char *cmd=myargv[0];//myargv[0]就是命令,后面的是它所带的参数
if(cmd:cd)...//cd,exit特殊情况特殊处理
if(cmd:exit...)
//其他普通命令:fork()+exec(让子进程执行exec)
}

2>strtok字符串分割函数介绍:

char*s=strtok(buff," ");//strtok有 一个指针记录分到哪里

s=strtok(NULL," ");//传空指针的原因是内部有一个记录的,是一个静态变量

s=strtok(NULL," ");

NULL s=strtok(NULL," ");

该函数中定义了一个静态变量,一个用来记录分割到哪部分的指针,设置为静态变量的原因是如果是局部变量,第一次使用完就会销毁掉,所以需要使用静态变量

(4)相关注意问题

1>buff[strlen(buff)-1]='\0';

这句主要是为了在退出时不会让程序没有反应,“strcmp(cmd,"exit")”,在这个比较函数中,如果不加上面那句那么键盘上输入"exit",是"exit\n"和"exit\0"比较,所以要用"buff[strlen(buff)-1]='\0';"把"exit\n"变为"exit\0"就会比较成功,让程序直接退出

下图是没加那句话时"cmd"的结果,第一张图片显示cmd="exit\n",所以第二张图片在运行时就不能退出,因为"exit\n"和"exit\0"不相等

下图是用gdb调试出来的结果,输入“exit”第二张也无法退出

加上“buff[strlen(buff)-1]='\0';”这句话之后,在键盘输入"exit"按回车之后那句话会把cmd变成"exit\0",所以会比较成功,程序会直接退出。

2>子进程里面的“exit(0)”能不能不要

不能。原因是如果把这句话删掉,假如输入一个错误的命令,子进程不会直接退出,输入"exit"后有子进程和父进程两个进程,所以需要输入两次"exit"这个程序才会退出。如果输入不止一个错误命令,那么就会产生很多进程,无法一次性退出。所以不能不要子进程中的"exit(0)"

3>获取用户名,主机名,路径,是管理员还是普通用户

printf("%s@%s %s%s")为了区分把系统的冒号改为空格

(5)具体代码实现

 #include<stdio.h>
   #include<assert.h>
   #include<unistd.h>
   #include<string.h>
   #include<stdlib.h>
   #include<pwd.h>
   #include<wait.h>
   #define ARG_MAX 10
   #define PATH_BIN "/home/stu/class04/test15/mybin/"
   //这个是自己写的命令存放位置,把它定义成宏

   //这个是字符串分割函数,对输入的命令实现分割
char* get_cmd(char*buff,char*myargv[])
{
    if(buff==NULL||myargv==NULL)
    {
    return NULL;
    }
   char *s=strtok(buff," ");//字符串分割函数strtok,上面有解释
   int i=0;
   while(s!=NULL)
   {
   myargv[i++]=s;
   s=strtok(NULL," ");
   }
   return myargv[0];
//这个是普通命令实现时调用的函数,如果是普通命令,就由子进程来实现,
顺序是先fork一个子进程,由子进程在相关命令存储位置去调用
void run_cmd(char*path,char*myargv[])
{
if(path==NULL||myargv==NULL)
{
return;
}
pid_t pid=fork();
assert(pid!=-1);
if(pid==-1)
{
printf("pid_t error\n");
}
if(pid==0)
{
//execvp(path,myargv);这句话本来的意思是直接从系统中调用命令,那么所有命令在自己写的
myshell系统中都会实现
char pathname[128]={0};
if(strncmp(path,"/",1)==0||strncmp(path,"./",2)==0)//这句话是模仿系统,"."和".."
文件不打印,这两个文件是系统中默认存在的不会打印,所以自己在处理的时候也不打印
{
 strcpy(pathname,path);
}
 else
 {
 strcpy(pathname,PATH_BIN);
 strcat(pathname,path);
 }
 execv(pathname,myargv);
 perror("execvp error\n");
 exit(0);
 }
 else
 {
 wait(NULL);//防止出现僵死进程
 }
 }
 void printf_info()//获取用户名,主机名,路径,是管理员还是普通用户
 {
     char*user_str="$";
     int user_id=getuid();
     if(user_id==0)
    {
     user_str="#";
     }
     struct passwd*ptr=getpwuid(user_id);
     if(ptr==NULL)
     {
     printf("mybash1.0>> ");
     fflush(stdout);
     return;
     }
     char hostname[128]={0};
     if(gethostname(hostname,128)==-1)
     {
      printf("mybash1.0>> ");
     fflush(stdout);
     return;
     }
     char dir[256]={0};
     if(getcwd(dir,256)==NULL)
     {
      printf("mybash1.0>> ");85     fflush(stdout);
     return;
     }
     //下面是字体颜色的设置
 printf("\033[1;32m%s@%s\033[0m   \033[1;34m %s\033[0m%s",ptr->pw_name,hostna    me,dir,user_str);
 fflush(stdout);
 }

 
 int main()
 {
 while(1)
 {
 printf_info();
 fflush(stdout);
 char buff[128]={0};
 fgets(buff,128,stdin);
 buff[strlen(buff)-1]='\0'; 102 char*myargv[ARG_MAX]={0};
 char*cmd=get_cmd(buff,myargv);
 if(cmd==NULL)
 {
 continue;
 }
 //两个内置命令
 else if(strcmp(cmd,"cd")==0)
 {
  if(myargv[1]!=NULL)
  {
 if(chdir(myargv[1])==-1)
 {
 perror("cd err!\n");
 }
 }
 }
 else if(strcmp(cmd,"exit")==0)
 {
     break;
 }
 //其他普通命令统一调用函数实现
 else
 {
    run_cmd(buff,myargv);
 }
 }
 exit(0);
 }

(6)mybin中部分代码实现

1>clear.c的实现

#include<stdio.h>

int main()
{
printf("\033[2J\033[0;0H");
}

2>ls.c的实现

#include<stdio.h>
 #include<stdlib.h>
 #include<unistd.h>
 #include<dirent.h>
 #include<string.h>
 #include<sys/stat.h>
 int main()
 {
 char path[256]={0};
 if(getcwd(path,256)==NULL)
 {
 perror("getcwd error\n");
 exit(1);
 }
 DIR*pdir=opendir(path);
 if(pdir==NULL)
 {
 printf("opendir error!\n");
 exit(0);
 }
 struct dirent*s=NULL;
 while((s=readdir(pdir))!=NULL)
 {
    if(strncmp(s->d_name,".",1)==0)
     {
     continue;
     }
     struct stat filestat;
     stat(s->d_name,&filestat);
     if(S_ISDIR(filestat.st_mode))
     {
     printf("\033[1;34m%s\033[0m  ",s->d_name);
     }
     else
     {
         if(filestat.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(pdir);
exit(0);
}

3>mycp.c的实现

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc,char*argv[])
{
if(argc!=3)
{
printf("argc error\n");
}
char *file_name=argv[1];
char *newfile_name=argv[2];
int fdr=open(file_name,O_RDONLY);
int fdw=open(newfile_name,O_WRONLY|O_CREAT,0600);
if(fdr==-1||fdw==-1)
{
exit(0);
}
char buff[256]={0};
int num=0;
while((num=read(fdr,buff,256))>0)
{
 write(fdw,buff,num); 24 }
 close(fdr);
 close(fdw);
 exit(0);
 }

4>mykill.c的实现

#include<stdio.h>
 #include<stdlib.h>
 #include<assert.h>
 #include<signal.h>
 #include<unistd.h>
 int main(int argc,char*argv[])
 {
 if(argc!=3)
 {
  printf("argc error!\n");
  return -1;
 }
 int pid=0;
 int sig=0;
 sscanf(argv[1],"%d",&pid);
 sscanf(argv[2],"%d",&sig);
 if(kill(pid,sig)==-1)
 {
 printf("kill error!\n");
 }
 }

5>myps.c的实现

#include<stdio.h>
 #include<stdlib.h>
 #include<unistd.h>
 int main(int argc,char*argv[],char*envp[])
 {
 printf("main pid=%d\n",getpid());
 char*myargv[]={"ps","-f",0};
 execve("/usr/bin/ps",myargv,envp);
 exit(0);
 }

6>pwd.c的实现

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

int main()
{
char path[256]={0};
if(getcwd(path,256)==NULL)
{
 perror("getcwd error\n");
 exit(1);
 }
 printf("%s\n",path);
 exit(0);
 }           

(7)命令解释器实现演示

mybin里面的命令:

自己写的和系统的用冒号和空格做了区分:

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值