Linux实现一个简单的shell-进程控制

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

一、shell的简单实现-C

shell运行的原理:子进程执行命令,父进程等待&&解析命令
分为五步进行执行
1.打印$前面的信息
2.读取用户输入命令
3.分割命令进行解析
4.判断TODO(比如cd)
5.创建子进程进行进程程序替换来执行命令,父进程接收

打印命令行提示符的缓冲区问题

printf("[zhupi@localhost myshell]# ");
fflush(stdout);
memset(cmd_line,0,sizeof cmd_line);

直接打印没有\n,会先放在缓冲区,缓冲区存放到一定大小才会刷新
可以通过fflush(stdout);来刷新缓冲区

命令行字符解析

思路1:写一个指针数组,空格置为\0就可以了,多个指针指向子字符串起始位置
思路2:C语言的strtok函数(string token字符串分割)

char*strtok(char*str,const char*delim)//delim是需要的分隔符

比如分割"ls -a -l -i"

#define SEP " "//分隔符
char*g_argv[NUM];//#define NUM 32

g_argv[0]=strtok(cmd_line,SEP);
int index=1;
while(g_argv[index++]=strtok(NULL,SEP));
//strtok如果解析的是相同字符串,那么后面第一个参数是NULL,不然又重新开始了
//解析到最后返回的是NULL

子进程使用exec系列函数的选择

首先,不知道命令的位置,所以需要去PATH环境变量找,其次,g_argv已经有了命令分割出来的指针数组,所以综合看来选择execvp
p表示环境变量,v可以看做需要传一个数组

cd切换路径的问题

系统调用接口chdir(change working director)

if(strcmp(g_argv[0],"cd")==0)
{
	if(g_argv[1] chdir(argv[1]));
	//argv[0]是命令,argv[1]是路径,因为它是cd
}

TODO

shell执行的命令有两种
1.第三方提供的在磁盘中的二进制可执行程序,比如ls,top等等,只需要 程序替换来调用即可执行
2.shell内部的命令,这些需要由父进程来执行,因为有些命令是要影响shell本身的,比如cd,export等等
这些命令就叫做TODO,内置命令,内建命令

因为子进程是不能够影响父进程的,如果cd由子进程来切换路径,那么只有他自己能看到修改后的路径,如果export由子进程来添加环境变量,那么只有它自己能看到添加的环境变量

#include <stdlib.h>
char*getenv(const char*name);//根据环境变量名获取环境变量
int putenv(char*string);//修改环境变量 MYVAL=101

所以说,可以 在分割完命令之后,进行TODO的判断,让父进程进行执行

if(strcmp(g_argv[0],"export")==0&&g_argv[1]!=NULL)
{
	strcpy(g_myval/*存储设置的环境变量,因为cmd_line会被清理带哦*/,g_argv[1]);
	putenv(g_myval);
	continue;//continue之后,下一次输入命令就会清空掉cmd_line,所以需要先保存
}

因为myshell是shell的子进程,所以是不能影响shell的环境变量的,所以用shell来看看不到,其次,进程程序替换是会替换掉代码和数据,但是有特例,比如环境变量就不会被替换(与系统相关的东西)

二、完整代码-C

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUM 1024
#define SISE 32
#defien SEP " "//分隔符
char cmd_line[NUM];//命令读取

char*g_argv[SIZE];//命令分割指针数组
char g_myval[64];//用于保存设置的环境变量
int main()
{
	//extern char** environ;
	while(1)
	{
		printf("[zhupi@localhost myshell]# ");
		fflush(stdout);
		memset(cmd_line,0,sizeof cmd_line);//sizeof(cmd_line) sizeof cmd_line
		//等待输入命令
		if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)
		{
			//fgets输入少了没关系,输入多了会受限制
			continue;
		}
		cmd_line[strlen(cmd_line)-1]=0;//去掉最后的回车
		
		//解析 strtok
		g_argv[0]=strtok(cmd_line,SEP);
		int index=1;
		if(strcmp(g_argc[0],"ls")==0)
		{
			g_argv[index++]="-color=auto";//如果是ls,添加自动配色
		}
		while(g_argv[index++]=strtok(NULL,SEP));
		if(strcmp(g_argv[0],"export")==0&&g_argv[1]!=NULL)
		{
		strcpy(g_myval/*存储设置的环境变量,因为cmd_line会被清理带哦*/,g_argv[1]);
		putenv(g_myval);
		continue;//continue之后,下一次输入命令就会清空掉cmd_line,所以需要先保存
		}		

		if(strcmp(g_argv[0],"cd")==0)
		{
			if(g_argv[1]!=NULL) chdir(g_argv[1]);//g_argv[1]是path
			//chdir进行路径切换
		}
		
		pid_t id=fork();
		if(id==0)
		{
			printf("下面的功能让子进程进行");
			//printf("child:%s\n",getenv["环境变量名"]);
			execvp(g_argv[0],g_argv);//进行进程程序替换
			//p表示在环境变量中查找
			exit(1);//替换成功将不执行,失败则进程退出,返回1,表示执行出错
		}
		//子进程 需要程序替换,所以父进程不用else来判断
		int status=0;
		pid_t ret =waitpid(id,&status,0);
		if(ret) printf("exit code:%d\n",WEXITSTATUS(status));//宏

	}
}

在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值