【Linux】12. 模拟实现shell

简介: 【Linux】12. 模拟实现shell

回顾

在之前的学习过程中,我们掌握了进程的相关概念,冯诺依曼体系结构,进程地址空间概念,进程状态,进程控制,进程退出,进程替换......等等一系列的基础知识,这些基础知识让我们清楚的知道进程从加载到运行到接收退出信息,操作系统是如何一步步进行控制的,也了解到相关操作系统的相关概念。
我们还学习到通过进程替换的方式,让操作系统使子进程执行另一个全新的进程却并未创建子进程的操作
在这里插入图片描述
废话不多说,直接开整!

1. 输出提示符

[hx@VM-12-2-centos myshell]$ cat myshell.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>

int main()
{
   
   
  printf("用户名@主机名 当前路径# ");
  fflush(stdout);
  sleep(10);
  return 0;
}

因为printf当中未带\n,所以要fflush(强制刷新缓冲区,将缓冲区的数据读取到显示器上)
在这里插入图片描述

2. 获取用户输入

在这里插入图片描述

3. 解决输入结尾带\n的问题

[hx@VM-12-2-centos myshell]$ cat myshell.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>

#define NUM 1024
char lineCommand[NUM];

int main()
{
   
   
  // 输出提示符
  printf("用户名@主机名 当前路径# ");
  fflush(stdout);

  // 获取用户输入
  char* s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
  assert(s != NULL);
  // 清除最后一个\n
  lineCommand[strlen(lineCommand)-1] = 0;
  printf("test:%s\n",lineCommand);
  return 0;
}

在这里插入图片描述

4. 字符串切割

在这里插入图片描述

5. 测试切割是否成功

采用条件编译的方式进行测试,后续不想要测试随时取消#define定义值即可

[hx@VM-12-2-centos myshell]$ cat myshell.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM];

int main()
{
   
   
  // 输出提示符
  printf("用户名@主机名 当前路径# ");
  fflush(stdout);

  // 获取用户输入
  char* s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
  assert(s != NULL);
  // 清除最后一个\n
  lineCommand[strlen(lineCommand)-1] = 0;
  // printf("test:%s\n",lineCommand);

  // 字符串切割
  myargv[0] = strtok(lineCommand," ");
  int i = 1;
  while(myargv[i++] = strtok(NULL," "));

  //测试切割是否成功
#ifdef DEBUG 
  // myargv数组中最后1个是NULL 正好拿来判断循环结束
  for(int i = 0; myargv[i];i++)
  {
   
   
    printf("myargv[%d]:%s\n",i,myargv[i]);
  }
#endif
  return 0;
}

在这里插入图片描述

6. 嵌套循环

在这里插入图片描述

7. 执行命令

在这里插入图片描述
所以,到这里我们也就知道了shell底层执行命令行的原理:通过提取命令行上的指令对其分析(切割子串),再通过exec*系列接口调用所对应的命令来显示到显示器上。
乍一看好像已经实现完成了,还存在什么缺陷嘛?
在这里插入图片描述
ls在执行时并没有颜色上的区分

8. ls -- 添加颜色

在这里插入图片描述

9. 内建命令cd

9.1 当前路径的认识

在这里插入图片描述
在这里插入图片描述
==但这同样没有解决myshell当中cd切换不了路径的问题鸭,那么该如何理解呢?==
shell在进行执行时,都是先通过fork创建子进程来执行对应的命令,那么也就是子进程执行的cd命令,子进程也存在自己的工作目录,当我们进行cd命令时更改的是子进程我的工作目录,当子进程执行完毕时,由父进程接收其返回值,之后使用的还是父进程shell,所以我们在父进程shell中观察不到子进程切换路径的现象

说了这么多,该如何操作实现呢?

9.2 增加内建命令cd的判断

在这里插入图片描述
==对于这种不需要子进程执行,而是让shell执行的命令,我们称之为内建/内置命令==

10. 内建命令echo $?

在这里插入图片描述

11. 最终代码

如下所示:

[hx@VM-12-2-centos myshell]$ cat myshell.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM];
int lastCode = 0;
int lastSig = 0;

int main()
{
   
   
  while(1){
   
   
    // 输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);

    // 获取用户输入
    char* s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
    assert(s != NULL);
    // 清除最后一个\n
    lineCommand[strlen(lineCommand)-1] = 0;
    // printf("test:%s\n",lineCommand);

    // 字符串切割
    myargv[0] = strtok(lineCommand," ");
    int i = 1;
    if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0)
    {
   
   
      myargv[i++] = (char*)"--color=auto";
    }

    while(myargv[i++] = strtok(NULL," "));

    if(myargv[0] != NULL && strcmp(myargv[0],"cd")==0)
    {
   
   
      if(myargv[1] != NULL)
      {
   
   
        chdir(myargv[1]);
      }
      continue;
    }
    if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0],"echo")==0)
    {
   
   
      if(strcmp(myargv[1],"$?")==0)
      {
   
   
        printf("%d,%d\n",lastCode,lastSig);
      }
      else 
      {
   
   
        printf("%s\n",myargv[1]);
      }
      continue;
    }


    //测试切割是否成功
#ifdef DEBUG 
    for(int i = 0; myargv[i];i++)
    {
   
   
      printf("myargv[%d]:%s\n",i,myargv[i]);
    }
#endif 

    // 执行命令
    pid_t id = fork();
    assert(id != -1);

    // 子进程
    if(id == 0)
    {
   
   
      execvp(myargv[0],myargv);
      exit(100);
    }
    // 父进程
    int status = 0;
    pid_t ret = waitpid(id,&status,0);
    assert(ret>0);
    lastCode = ((status>>8) & 0xFF);
    lastSig = (status & 0x7F);
  }
  return 0;
}
[hx@VM-12-2-centos myshell]$ cat Makefile 
myshell:myshell.c
    gcc -o myshell myshell.c -std=c99 # -DDEBUG

.PHONY:clean
clean:
    rm -f myshell

页内跳转

点击它跳转到 标题-页内跳转

相关文章
|
18天前
|
Linux Shell 开发工具
Shell的运行原理以及Linux当中的权限问题
Shell的运行原理以及Linux当中的权限问题
31 0
|
22天前
|
Web App开发 Java Linux
Linux之Shell基本命令篇
Linux之Shell基本命令篇
Linux之Shell基本命令篇
|
22天前
|
安全 Linux Shell
Linux SSH(Secure Shell)服务
Linux SSH提供安全网络协议,使用公钥加密技术确保远程服务传输安全。OpenSSH是实现SSH服务的免费开源工具,允许用户加密连接远程登录Linux服务器执行任务。SSH比Telnet更安全,防止数据被截获。SSH还支持端口转发和隧道,广泛应用于系统管理和网络维护,是安全远程访问服务器的重要工具。
21 1
|
23天前
|
Shell Linux UED
|
28天前
|
运维 Java Shell
Linux非常详细的shell运维脚本一键启动停止状态SpringBoot打成可运行jar包
Linux非常详细的shell运维脚本一键启动停止状态SpringBoot打成可运行jar包
28 0
|
2月前
|
Linux Shell 文件存储
【Linux技术专题】「夯实基本功系列」带你一同学习和实践操作Linux服务器必学的Shell指令(深入df和dh的区别和探索)
【Linux技术专题】「夯实基本功系列」带你一同学习和实践操作Linux服务器必学的Shell指令(深入df和dh的区别和探索)
37 1
|
20天前
|
存储 Shell Linux
【攻防世界】unseping (反序列化与Linux bash shell)
【攻防世界】unseping (反序列化与Linux bash shell)
|
21天前
|
Shell Linux
在linux shell脚本中root切换到普通用户执行脚本或命令的方法
在linux shell脚本中root切换到普通用户执行脚本或命令的方法
12 1
|
29天前
|
Shell Linux
Linux脚本Shell脚本来实现一次性kill多个端口的进程
Linux脚本Shell脚本来实现一次性kill多个端口的进程
5 0
|
29天前
|
Shell Linux
Linux的shell入门教程shell脚本入门教程
Linux的shell入门教程shell脚本入门教程
21 0