【Linux】实现一个专属定制的简单shell命令行

文章介绍了如何使用C语言实现一个简单的Shell命令行,主要涉及初始化命令行、获取用户输入、解析字符串、处理内建命令以及通过fork和execvp系统调用来执行外部命令。内建命令如cd需要由shell本身处理,而其他命令则在子进程中执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于进程控制实现简单shell命令行

实际上我们在命令行输入ls,就是-bash进程在调用fork(),而后对该子进程进行进程替换,让该子进程去执行ls的相关代码

所以我们要实现简单命令行,实际上只需下面几步

  1. 初始化命令行,打印提示信息

  2. 获取用户需求–获取用户输入的数据

  3. 解析用户需求–分析字符串

  4. 处理一些内建命令

  5. fork子进程进行进程替换完成用户的需求

1. 初始化命令行,打印提示消息

#define NUM 64
char commond[NUM];
//将命令行初始化好
memset(commond,'\0',sizeof(commond));
//1.打印提示和清理字符串
commond[0]=0; //这个方式可以O(1)方式清理字符串
printf("[lsh@hostname mydir]# ");
fflush(stdout);

其中需要使用fflush函数,因为我们平时的命令行,打印完提示信息都是没有进行换行,而如果我们不在打印语句中+\n,我们的语句并不会马上刷新出来,而是等待进程结束后,才会刷新出来,所以我们就可以使用fllush系统调用(本质是强制刷新缓冲区–后面会讲解),将数据刷新出来

2.获取用户输入的数据

 //2.获取命令 fgets
    fgets(commond,sizeof(commond),stdin);  //从默认入流中读取数据
    //将读取到的换行换为\0  一定要取消掉 不然影响后序的输入
    commond[strlen(commond)-1]='\0';
    //printf("%s\n",commond);  //检验输入成果

其中注意的主要是,我们结束输入时会获取回车,所以获取的子符串后面会多一个回车,需要将其去掉,会影响后面解析字符串

3.解析用户输入的字符串

//3.解析命令 strtok
    char *argv[Com_NUM]={NULL};
    int index=1;
    char *str=" "; //分割符
    argv[0]=strtok(commond,str);
    //如果还有命令行参数继续获取
    while(argv[index++]=strtok(NULL,str))
    {}

 //检验argv 检验分包的结果
   // for(int i=0;argv[i];++i)
   // {
   //   printf("argv[%d]: %s\n",i,argv[i]);
   // }
   // printf("\n");

4.处理一些内建命令

例如:cd …

	//4.在子进程执行内置命令4. TODO,内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
    //内建命令本质其实就是shell中的一个函数调用 	
	if(strcmp(argv[0],"cd")==0)
    {
      if(argv[1]!=NULL)
      {
        chdir(argv[1]);
      }
      continue;
    }

如果用户输入的cd …,本质是需要改变当前工作目录,而如果我们进行进程替换,cd …执行的其实是在子进程执行,并不会影响当前进程,需要被shell本身执行的就叫做内建命令

chdir

 int chdir(const char *path);

而实际上我们在命令行执行cd …时,也是调用了一个函数chdir,作用是将当前进程的路径变化到path

5.fork子进程进行进程替换

if(fork()==0)
    {
      //child
      execvp(argv[0],argv);
      exit(22);
    }
    int status=0;
    waitpid(-1,&status,0);
    printf("exit code:%d\n",(status>>8)&0xFF);

代码演示:

总代码:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>

#define NUM 64
#define Com_NUM 128
int main()
{
  char commond[NUM];
  //将命令行初始化好
  memset(commond,'\0',sizeof(commond));
  while(1)  //从默认入流中读取数据
  {
    //1.打印提示和清理字符串
    commond[0]=0; //这个方式可以O(1)方式清理字符串
    printf("[lsh@hostname mydir]# ");
    fflush(stdout);
    //2.获取命令 fgets
    fgets(commond,sizeof(commond),stdin);  //从默认入流中读取数据
    //将读取到的换行换为\0  一定要取消掉 不然影响后序的输入
    commond[strlen(commond)-1]='\0';
    //printf("%s\n",commond);
    //3.解析命令 strtok
    char *argv[Com_NUM]={NULL};
    int index=1;
    char *str=" ";
    argv[0]=strtok(commond,str);
    //如果还有命令行参数继续获取
    while(argv[index++]=strtok(NULL,str))
    {}
    //4.在子进程执行内置命令4. TODO,内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
    //内建命令本质其实就是shell中的一个函数调用
    if(strcmp(argv[0],"cd")==0)
    {
      if(argv[1]!=NULL)
      {
        chdir(argv[1]);
      }
      continue;
    }
    //5.在子进程中执行第三方命令
    if(fork()==0)
    {
      //child
      execvp(argv[0],argv);
      exit(22);
    }
    int status=0;
    waitpid(-1,&status,0);
    printf("exit code:%d\n",(status>>8)&0xFF);
  }
  return 0;
}
 execvp(argv[0],argv);
      exit(22);
    }
    int status=0;
    waitpid(-1,&status,0);
    printf("exit code:%d\n",(status>>8)&0xFF);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值