【Linux】实现一个简单的shell命令解释器

【1】实现的功能

我们所做的这个简单的shell命令解释器可以实现简单的常用的基本命令,如ls、pwd、cd、cd - 、cd ~ 等

根据简单命令的定义,它的第一个参数是要执行的命令,后面的参数作为该命令的参数。
要执行的命令有两种情况:

一种是外部命令: 也就是对应着磁盘上的某个程序,例如 pwd、ls等等。对于这种外部命令,我们首先要到指定的路径下找到它,然后再执行它。

另一种是内部命令:内部命令并不对应磁盘上的程序,例如cd等等,它需要shell自己来决定该如何执行。例如对 cd 命令,shell就应该根据它后面的参数改变当前路径。

对于外部命令,需要创建一个子进程来执行它,本质就是fork+exec

【2】代码

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pwd.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX 10
#define STRLEN 128

#define PATH "/bin/"	//系统bin路径位置

char OLDPWD[STRLEN]={0};	//记录上一次的命令,为了cd -这条命令

//================================================================================
//每次敲回车输出当前所在用户信息
//普通用户和root用户的提示符区别
void Printf_Info()	
{
	char flag='$';
	struct passwd *pw=getpwuid(getuid());
	assert(pw!=NULL);

	//uid为0则为root用户
	if(pw->pw_uid==0)	
	{
		flag='#';
	}
	struct utsname hostname;	//主机名
	uname(&hostname);

	char node[STRLEN]={0};
	strcpy(node,hostname.nodename); //获取网络上的名称

	char* name=strtok(node,".");

	//获取绝对路径
	char path[STRLEN]={0};
	getcwd(path,STRLEN-1);

	char*p=path+strlen(path);	//p指向绝对路径的末尾

	while(*p!='/')
	{
		p--;
	}

	//p指向路径末尾往前的第一个‘/’位置处
	if(strlen(path)!=1)
	{
		p++;	//++前,p->'/'
	}
	if(strcmp(path,pw->pw_dir)==0)
	{
		p="~";
	}
	printf("\033[;32mMyBash[%s@%s %s]%c\033[0m",pw->pw_name,name,p,flag);
	//  \033[47;31mThis is a color test\033[0m   设置打印结果的颜色
    fflush(stdout);

}
//================================================================================
void Mycd(char*path)
{
	//第一个字符串为cd而第二为空 如:cd 则结束本轮循环
	if(path==NULL)	
	{
		exit(0);
	}

	//cd ~ 回到用户根目录
	if(strcmp(path,"~")==0) 	
    {
        struct passwd*pw=getpwuid(getuid());
        path=pw->pw_dir;
    }

    //cd - 回到上一次的位置
    if(strcmp(path,"-")==0)   
    {
       	//若是第一次输入命令,则cd -命令不存在!
        if(strlen(OLDPWD)==0)
        {
            printf("\033[;31mMyBash:cd:OLDPWD not set\n\033[0m");
            return ;
        }
        //否则把上一次的命令给path
        path=OLDPWD;
    }

    //getpwd记录当前工作目录的绝对路径
    char oldpwd[STRLEN]={0};
    getcwd(oldpwd,STRLEN-1);

	if(-1==chdir(path))//反之则不是空,则通过chdir系统调用进入到该目录中
    {
        char err[128]="\033[;31mMybash: cd \033[0m";
        strcat(err,path);

        perror(err);
    }
    //每次执行完cd命令后,把工作路径赋给OLDPWD	
    strcpy(OLDPWD,oldpwd);
}
//================================================================================
//命令分割函数
void Strtok_cmd(char*buff,char*myargv[])
{
	char *s=strtok(buff," ");	//分割输入的字符串
	if(s==NULL)		//如果s为空,则进入下一轮循环
	{
		exit(0);
	}

	myargv[0]=s;	//把分割出来的第一个字符串放在myargv[0]中
	int i=1;
	while((s=strtok(NULL,""))!=NULL)	//把后续分割出来的字符串依次存放在数组中
	{
		myargv[i++]=s;
	}
}
//===============================================================
int main()
{
	while(1)
	{
		char buff[128]={0};

		Printf_Info();

		//从终端获取命令存入buff中
		fgets(buff,128,stdin);	
		buff[strlen(buff)-1]=0;

		char *myargv[MAX]={0};

		//分割输入的命令
		Strtok_cmd(buff,myargv);

		//如果输入exit则退出循环
		if(strcmp(myargv[0],"exit")==0)		
		{
			exit(0);
		}

		//如果分割出来的第一个字符串为cd 
		else if(strcmp(myargv[0],"cd")==0)	
		{
			Mycd(myargv[1]);
			continue;
		}
		
		//若是系统调用,直接替换fork+exec
		pid_t pid=fork();
		assert(pid!=-1);

		if(pid==0)
		{
			char path[256]={0};
			if(strncmp(myargv[0],"./",2)!=0 && strncmp(myargv[0],"/",1)!=0)
			{
				//先把路径放入path中
				strcpy(path,PATH);
			}

			//进行命令拼接,路径+名称
			strcat(path,myargv[0]);
			//替换进程	例如:/bin/ls
			execv(path,myargv);		
			perror("\033[;31mexecv error\033[0m");
		}
		//处理僵死进程
		else
		{
			wait(NULL);
		}
	}
}

【3】运行结果

运行结果如下
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
异常处理如下:

若是第一次运行程序,则不能使用cd - 命令,因为此时还没有历史路径
若进入一个不存在的目录则会报错,没有这个文件或目录
若直接输入一个不存在的无法识别的命令,也会报错。
在这里插入图片描述

  • 5
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 33
    评论
好的,我会尽力回答您的问题。 首先,让我们来了解一下Linux解释器的原理。Linux解释器一个命令解释器,通常称为shell。它的主要作用是解释命令行输入的指令,并将其转化为计算机可以理解的指令来执行。Linuxshell有很多种,比如sh、bash、zsh等。 在实现一个简单shell之前,我们需要了解一些基本的shell编程知识。Shell编程通常使用的是Bash语言,它是一种基于命令行的编程语言。Bash语言的基本语法和C语言比较类似,只是语法更为简单。 接下来,我们来实现一个简单shell命令解释器)。 1. 首先,我们需要创建一个脚本文件,比如“myscript.sh”,并在文件的第一行添加以下代码: ``` #!/bin/bash ``` 这行代码告诉Linux系统,该脚本文件需要使用Bash解释器来执行。 2. 接下来,我们需要定义一些变量。变量可以用来存储命令行输入的参数,以及一些程序中需要使用的常量。比如: ``` # 定义一个常量 readonly MY_CONST="Hello World" # 定义一个变量 MY_VAR="" # 获取命令行输入的参数 MY_ARGS="$@" ``` 3. 然后,我们需要编写一些函数来实现具体的功能。比如: ``` # 打印一个字符串 function print() { echo "$1" } # 复制一个文件 function copy_file() { cp "$1" "$2" } ``` 4. 最后,我们需要编写一些代码来调用这些函数。比如: ``` # 打印常量 print "$MY_CONST" # 复制文件 copy_file "$1" "$2" ``` 这样,我们就完成了一个简单shell。当我们在命令行输入“./myscript.sh file1 file2”的时候,程序就会复制file1文件到file2文件中。 当然,这只是一个简单的示例,实际上,shell编程可以实现非常复杂的功能。如果您想深入学习shell编程,建议您阅读一些相关的书籍和教程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值