• 【Linux】自己实现一个bash进程


    bash就是命令行解释器,就是Linux操作系统让我们看到的,与用户进行交互的一种外壳(shell),当然了bash也是一个进程,它有时候就是通过创建子进程来执行我们输入的命令的。这无疑就离不开我们上篇博客所说的进程程序替换,就是让子进程去替换我们的命令进程,知道了它的原理,我们就可以试着自己写一个bash进程。

    我们可以大体上对于整个过程来细分一下:

    1.bash就是先打印命令行提示符,就是下面这个东西,获取用户输入的命令

    命令行提示符由  [用户名@主机名 路径]#  这些构成

    2.对于用户输入的字符串进行分割成命令

    3.执行这个命令

    其实大体上就是这三步,下面还有很多的细节问题,我们遇到再说

    我们目前写的这个代码的结果是

    为什么打印了两个换行?我们了解一下fgets这个函数

    因为fgets会读入一个换行,就是我们敲完abc后的换行,它是不应该有的,我们要给它去掉

    这样就可以把换行改成0了。

    因为bash就是一只在等待用户输入指令,所以我们要把我们的程序写成一个循环。

    还有一个问题,就是当我们什么都不输入,直接回车时,我们其实就没必要在执行下边了,直接回到循环最开始就可以,于是我们可以做一个判断就是如果输入的命令的长度为0,那么就直接回到循环最开始处,这里的长度可以让Interactive的返回值来给。

    下一步就是对于字符串进行分割:

    我们分割的话,函数可以将收到的字符串分割后放到一个全局变量中,方便后续的使用,我们C语言有一个分割字符串的函数,叫strtok

    分割完了之后,我们就可以开始执行命令了,就是让子进程执行,父进程等待。我们这里先是一些普通的需要子进程去执行的命令,因为有的命令需要父进程去执行,比如cd,需要父进程去切换路径,子进程是不行的

    那么截止到现在,我们的程序已经可以像bash一样处理一些命令了,但是还不够,因为有一些内建命令,就是需要父进程去执行的:

    这些命令,我们要在代码被子进程执行前先行判断,如果是内建命令,那么让父进程去执行,否则再让子进程去执行

    我们这里先以cd命令为例,如果判断出来了用户就是要输入cd命令,如果后边什么都没有,那么默认是回到家目录,如果有,那就chdir到那个目录,并且不要忘记命令行提示符可是一直通过环境变量PWD来打印我们当前所在目录的,所以我们还要通过putenv把PWD环境变量改一下,它默认是会覆盖的

    之后我们要知道export也是一个内建命令,export的作用是给自己设置一个环境变量,如果给子进程设置,那显然是不合理的,所以我们也需要处理一下。

    下一个就是echo,我们的echo通常会有这么几种用法:

    1.echo后什么都不加

    2.后加$?表示打印最近一次进程的退出码

    3.后随便打印一串字符

    4.后加$环境变量,就打印环境变量的内容

    下面是所有的代码,有不足的可以添加:

    1. 1 #define _XOPEN_SOURCE
    2. 2 #include
    3. 3 #include
    4. 4 #include
    5. 5 #include
    6. 6 #include
    7. 7 #include
    8. 8 #include
    9. 9 #define SIZE 1024
    10. 10 #define MAX_ARGC 30//最大命令行字符串个数
    11. 11 #define SEP " "//设置分隔符为空格
    12. 12 char*argv[MAX_ARGC];
    13. 13 int lastcode;//最近一次进程退出码
    14. 14 int Interactive(char commandline[],int size)
    15. 15 {
    16. 16 printf("[%s@%s %s]$ ",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));
    17. 17 fgets(commandline,size,stdin);
    18. 18 commandline[strlen(commandline)-1]='\0';
    19. 19 return strlen(commandline);
    20. 20 }
    21. 21 void Splitstr(char commandline[])
    22. 22 {
    23. 23 int i=0;
    24. 24 argv[i++]=strtok(commandline,SEP);
    25. 25 while(argv[i++]=strtok(NULL,SEP));
    26. 26 if(strcmp("ls",argv[0])==0)
    27. 27 {
    28. 28 argv[i-1]="--color";
    29. 29 argv[i]=NULL;
    30. 30 }
    31. 31 }
    32. 32 void Execute()
    33. 33 {
    34. 34 if(strcmp("ll",argv[0])==0&&!argv[1])//特别处理ll
    35. 35 {
    36. 36 argv[0]="ls";
    37. 37 argv[1]="-l";
    38. 38 }
    39. 39 pid_t id=fork();
    40. 40 if(id==0)
    41. 41 {
    42. 42 execvp(argv[0],argv);
    43. 43 printf("mybash: ");
    44. 44 for(int i=0;argv[i];i++)printf("%s ",argv[i]);
    45. 45 printf(": not found your command\n");
    46. 46 exit(2);
    47. 47 }
    48. 48 int status=0;
    49. 49 pid_t rid=waitpid(id,&status,0);
    50. 50 if(rid>0)lastcode=WEXITSTATUS(status);
    51. 51 }
    52. 52 int Bulidincmd()
    53. 53 {
    54. 54 int ret=0;
    55. 55 if(strcmp("cd",argv[0])==0)
    56. 56 {
    57. 57 ret=1;
    58. 58 char*target=argv[1];
    59. 59 if(!target)target=getenv("HOME");
    60. 60 chdir(target);
    61. 61 char tmp[SIZE];
    62. 62 snprintf(tmp,SIZE,"PWD=%s",target);
    63. 63 putenv(tmp);
    64. 64 }
    65. 65 else if(strcmp("export",argv[0])==0)
    66. 66 {
    67. 67 ret=1;
    68. 68 if(argv[1])
    69. 69 {
    70. 70 char tmp[SIZE];
    71. 71 strncpy(tmp,argv[1]+1,strlen(argv[1])-2);
    72. 72 putenv(tmp);
    73. 73 }
    74. 74 }
    75. 75 else if(strcmp("echo",argv[0])==0)
    76. 76 {
    77. 77 ret=1;
    78. 78 if(argv[1]&&argv[1][0]=='$')
    79. 79 {
    80. 80 if(argv[1][1]=='?'&&!argv[2])
    81. 81 {
    82. 82 printf("%d\n",lastcode);
    83. 83 lastcode=0;
    84. 84 }
    85. 85 else
    86. 86 {
    87. 87 char* tmp=getenv(argv[1]+1);
    88. 88 if(tmp)printf("%s\n",tmp);
    89. 89 }
    90. 90 }
    91. 91 else
    92. 92 {
    93. 93 for(int i=1;argv[i];i++)printf("%s ",argv[i]);
    94. 94 printf("\n");
    95. 95 }
    96. 96 }
    97. 97 lastcode=0;
    98. 98 return ret;
    99. 99 }
    100. 100 int main()
    101. 101 {
    102. 102 while(1)
    103. 103 {
    104. 104 char commandline[SIZE];
    105. 105 //打印命令行提示符,获取用户输入的命令字符串
    106. 106 int n=Interactive(commandline,SIZE);
    107. 107 if(n==0)continue;
    108. 108 //分割字符串成命令行参数
    109. 109 Splitstr(commandline);
    110. 110 //处理内建命令
    111. 111 n=Bulidincmd();
    112. 112 if(n==1)continue;
    113. 113 //执行命令
    114. 114 Execute();
    115. 115 }
    116. 116 return 0;
    117. 117 }

  • 相关阅读:
    SpringCloud链路追踪——Spring Cloud Sleuth 和 Zipkin 介绍 & Windows 下使用初步
    (十)Java算法:归并排序(详细图解)
    操作系统学习笔记
    Cesium之home键开关及相机位置设置
    贝茄莱(B&R)实时以太网通讯测试
    python使用matplotlib可视化subplots子图、subplots绘制子图并为可视化的多个子图设置共享的X轴
    【IPC 通信】信号处理接口 Signal API(7)
    AudioOptions
    Java应用层数据链路追踪(附优雅打印日志姿势)
    面试官: AMS在Android起到什么作用,简单的分析下Android的源码
  • 原文地址:https://blog.csdn.net/2201_76024104/article/details/139219885