Linux——仿写Bash

Bash:与用户交互的程序——命令解析器

Bash的基本工作流程:{

                                        1.打印提示信息

                                         2.等待用户输入命令 

                                              (1)对用户输入的字符串进行拆分

                                              (2)对命令进行分类:内置命令(cd+exit)和外置命令(ls,pwd,ps......)

                                         3.调用fork复制进程,子进程执行用户输入的命令对应的程序

                                           后台:父进程直接执行下一次循环

                                            前台:父进程等待子进程结束

                                       }

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <assert.h>
#include <sys/utsname.h>
#include <unistd.h>

//1.输出提示信息
void PrintPrompt()
{
   struct passwd *pw = getpwuid(getuid());
   assert(pw !=NULL);
   char flag = '$';

   if(getuid() == getpwnam("root")->pw_uid)//如果相等,则当前用户就是root用户
	{
	   flag = '#';
	}
  
   struct utsname host;  //获取主机信息
   uname(&host);

   char pwd[128] = {0};
   getcwd(pwd,127);

   char *p = NULL;

   //判断当前工作位置是否是家目录
   if(strcmp(pwd,pw->pw_dir) == 0)
   {
     p = "~";
   }
   else
   {
       p = pwd +strlen(pwd);
       while(*p != '/')
       {
           p--;
       }

       if(strlen(p) > 1)  //判断当前工作位置是否是根目录
       {
            p++;
        }
   }
   printf("[%s@%s %s]%c",pw->pw_name,host.nodename,p ,flag);
}
//2.将命令按照空格进行切割,分别存储到字符指针数组中
void CutCommand(char *cmd,char* CutCmd[])
{
     int count = 0;
     char *p = strtok(cmd,"");
     while(p!=NULL)
	 {
	   CutCmd[count++] = p;
	   p = strtok(NULL,"");
	 }
}
//仿写cd命令
void PerfomCd(char *path)
{

	char newpath[128] = {0};
	static char oldpath[128] = {0};
	struct passwd* pw = getpwuid(getuid());
	assert(pw != NULL);
	// 家目录
	if (path == NULL)
	{
		// 给newpath中填充家目录
		strcpy(newpath, pw->pw_dir);
	}
	else if (strncmp(path, "~", 1) == 0)
	{
		// 给newpath填充家目录后面跟上path的”~“后面的内容
		strcpy(newpath, pw->pw_dir);
		strcat(newpath, path + 1);
	}
	else if (strncmp(path, "-", 1) == 0 && strlen(path) == 1)//cd -:切换到上一次的工作目录
	{
		// 给newpath填充上一次所在位置
		if (strlen(oldpath) == 0)
		{
			printf("cd : OLDPWD not set\n");
			return;
		}
		strcpy(newpath, oldpwd);
	}
	else
	{
		//给newpath填充path中的内容
		strcpy(newpath, path);
	}

	char nowpath[128] = { 0 };
	getcwd(nowpath, 127);

	if (-1 == chdir(newpath))
	{
		perror(path);
		return;
	}

	memset(oldath, 0, 128);
	strcpy(oldpath, nowpath);
}

//3.分类命令,如果是内置命令则交给后面的流程处理
int ClassOfCommand(char *CutCmd[])
{

  if(strncmp(CutCmd[0],"exit",4) == 0)//比较,如果相等
  {
     exit(0);
  }
  if(strncmp(CutCmd[0],"cd",2) == 0)//比较,如果相等
  {
       PerfomCd(CutCmd[1]);
       return 1; //内置命令
  }

  return 0;  //外置命令

}
//处理外置命令
void PerformOutCommand(char *CutCmd[])
{
	pid_t pid = fork();
	assert(pid != -1);

     if(pid == 0)  //子进程执行用户输入的命令对应的程序
	 {
		 char path[128] = "/home/stu/hw/mybin/";
		 //char path[128] = "/bin/"; //测试用

		 if (strstr(CutCmd[0], "/") != NULL) //  用户输入的执行命令中有路径
		 {
			 memset(path, 0, 128);
		 }
		 strcat(path, CutCmd[0]);

		 execv(path, CutCmd);  //  进程替换
		 perror("execv : ");
		 exit(0);  //   当execv替换失败后,子进程必须结束

	 }
	 else
	 {
		 wait(NULL);
	 }

}
int main()
{
   while(1)
  {
  PrintPrompt();            // 1. 输出提示信息

  char cmd[128]={0};        //得到用户输入的命令
  fgets(cmd,127,stdin);
  cmd[strlen(cmd) -1] = 0;   //去掉最后的回车符
  if(strlen(cmd) == 0)      //处理用户只是输入了回车的情况
  {
     continue;
  }

 char *CutCmd[10] = {0};    
 CutCommand(cmd,CutCmd);    //处理切割后的命令

  if(ClassOfCommand(CutCmd))
  {
       continue;
  }
  //如果能执行到此,说明用户执行的是外置命令

  PerformOutCommand(CutCmd);

  }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值