【Linux】bash命令解析器的实现

(一)bash流程框架图:

在这里插入图片描述

(二)代码实现

  • mybash.h
#ifndef MYBASH_H
#define MYBASH_H
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <pwd.h>
#include <sys/utsname.h>

typedef enum returncode
{
    //地址空
    ADDR_NULL = 0,
    //退出 1
    EXIT,
    //cd 2
    MYCD,
    //exec 3
    EXEC
}returncode;

//输出提示符信息
void OutPutInfo();

//从键盘获取命令
char* GetCmd();

//命令解析
returncode CmdResolve(char* cmd, char* cmdbuff[]);

//进程替换
void ChildExec(char* cmdbuff[], const char* cmd);

//处理僵尸进程
void Zombie();

//判断是否在后台运行
int IsBack(const char* cmdbuff);

//切换目录
void Mycd(const char* path);

#endif
  • mybash.c
#include "mybash.h"

//输出提示符信息
void OutPutInfo()
{
	// struct passwd
	// {
	// 	char *pw_name;	 /* username */
	// 	char *pw_passwd; /* user password */
	// 	uid_t pw_uid;	 /* user ID */
	// 	gid_t pw_gid;	 /* group ID */
	// 	char *pw_gecos;	 /* user information */
	// 	char *pw_dir;	 /* home directory */
	// 	char *pw_shell;	 /* shell program */
	// };
	char flg = '$';
	struct passwd* pw = getpwuid(getuid());
	if(pw == NULL)
	{
		return;
	}
	//判断是普通用户还是root用户
	if(pw->pw_uid == 0)
	{
		flg = '#';
	}
	/*
	*普通用户:
	*	用户名@主机名:当前工作路径名$
	*root用户
	*	root@主机名:当前工作路径名#
	*/
	//获取主机名
	struct utsname buff;
	uname(&buff);
	//获取当前工作路径的绝对路径
	char pathname[128] = {0};
	getcwd(pathname, 127);
	
	//家目录的长度
	int len_homepath = strlen(pw->pw_dir);
	char* p = pathname + len_homepath;
	//如果当前的工作路径中包含家目录,就使用"~"替换前面的家目录部分
	if(strncmp(pathname, pw->pw_dir, len_homepath) == 0)
	{
		memset(pathname, 0, len_homepath);
		strcat(pathname, "~");
		strcat(pathname, p);
	}
	printf("%s@%s:%s%c ",pw->pw_name,buff.nodename, pathname,flg);
	fflush(stdout);
}

//从键盘获取命令
char* GetCmd()
{
	char* cmd = (char*)malloc(sizeof(char) * 128);
	memset(cmd, 0, sizeof(char) * 128);
	fgets(cmd, 127, stdin);
	cmd[strlen(cmd) - 1] = '\0';	//去掉'\n'
	
	return cmd;
}

//命令解析
returncode CmdResolve(char* cmd, char* cmdbuff[])
{
	if(cmd == NULL || cmdbuff == NULL)
	{
		return ADDR_NULL;
	}
	//分割指令
	char *s = strtok(cmd, " ");
	int i = 0;
	while (s != NULL)
	{
		cmdbuff[i++] = s;
		s = strtok(NULL, " ");
	}
	//最后一个置空
	cmdbuff[i] = NULL;

	if(strcmp(cmdbuff[0], "exit") == 0)
	{
		//退出
		return EXIT;
	}

	if(strcmp(cmdbuff[0], "cd") == 0)
	{
		//切换目录
		return MYCD;
	}
	else
	{
		//进程替换
		return EXEC;
	}
}

//进程替换
void ChildExec(char* cmdbuff[], const char* cmd)
{
	if(cmdbuff == NULL || cmd == NULL)
		return;
	
	char pathname[128] = {"/home/jiege/mybash/bin/"};
	strcat(pathname, cmdbuff[0]);

	//判断是前台/后台运行
	int ret = IsBack(cmd);	//默认前台0
	

	pid_t pid = fork();
	if(pid == -1)
	{
		perror("fork err");
		return;
	}

	if(pid == 0)
	{
		execv(pathname, cmdbuff);
		perror("command not found");
		exit(0);
	}
	else
	{
		//前台运行
		if(ret == 0)
		{
			wait(NULL);
		}
		//后台运行通过注册的信号zombie去处理僵尸进程
	}
}
//处理后台的僵尸进程
void Zombie()
{
	wait(NULL);
}

//判断是否在后台运行
int IsBack(const char* cmd)
{
	if(cmd == NULL)
		return -1;
	if(strstr(cmd, "&") != NULL)
		return 1;
	return 0;
}

//切换目录
void Mycd(const char* path)
{
	if(path == NULL)
		return;
	if(chdir(path) == -1)
	{
		perror("cd err");
	}
}
  • main.c
#include "mybash.h"

int main()
{
	//处理后台的僵尸进程
	signal(SIGCHLD, Zombie);

	char* cmd = NULL;
	while(1)
	{	
		//输出提示符信息
		OutPutInfo();
		//从键盘获取命令
		cmd = GetCmd();
		//存放解析后的参数
		char* cmdbuff[32] = {0};
		//命令解析
		returncode ret = CmdResolve(cmd, cmdbuff);
		if(ret == EXIT)
		{
			free(cmd);
			cmd = NULL;
			break;
		}
		else if(ret == MYCD)
		{
			//切换路径
			Mycd(cmdbuff[1]);
			free(cmd);
			cmd = NULL;
		}
		//空地址
		else if(ret == ADDR_NULL)
		{
			free(cmd);
			cmd = NULL;
			continue;
		}
		else
		{
			//进程替换
			ChildExec(cmdbuff, cmd);
		}
		
	}

	return 0;
}
  • makefile
objects=main.o mybash.o 

mybash:$(objects)
	gcc -o mybash $(objects)

$(objects): mybash.h main.c mybash.c

.PHONY:clean
clean:
	rm -f mybash $(objects)

(三)最终项目文件结构

在这里插入图片描述
已上传githubhttps://github.com/HuTaoHub/mybash.git

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值