当前位置:   article > 正文

从零开始学习Linux(8)----自定义shell

从零开始学习Linux(8)----自定义shell

        shell从用户读入字符串“ls”,shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序,并等待这个进程结束。所以要写一个shell,需要循环以下过程:

        1. 获取命令行
        2. 解析命令行
        3. 建立一个子进程(fork)
        4. 替换子进程(execvp)
        5. 父进程等待子进程退出(wait)
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. #define SIZE 1024
  8. #define MAX_ARGC 64
  9. #define SEP " " // 命令参数之间的分隔符
  10. // 全局变量
  11. char *argv[MAX_ARGC]; // 用于存储解析后的命令行参数
  12. char pwd[SIZE]; // 当前工作目录
  13. char env[SIZE]; // for test
  14. int lastcode = 0; // 最后一个子进程的退出状态
  15. // 获取主机名
  16. const char* HostName()
  17. {
  18. char *hostname = getenv("HOSTNAME"); // 从环境变量中获取HOSTNAME
  19. if(hostname) return hostname; // 如果找到,返回主机名
  20. else return "None";// 否则返回"None"
  21. }
  22. // 获取用户名
  23. const char* UserName()
  24. {
  25. char *username = getenv("USER");
  26. if(username) return username; // 如果找到,返回用户名
  27. else return "None"; // 否则返回"None"
  28. }
  29. // 获取当前工作目录
  30. const char *CurrentWorkDir()
  31. {
  32. char *pwd = getenv("PWD"); // 应该是从环境变量中获取PWD
  33. if(pwd) return pwd; // 如果找到,返回当前工作目录
  34. else return "None"; // 否则返回"None"
  35. }
  36. // 获取用户主目录
  37. char *Home()
  38. {
  39. return getenv("HOME"); // 从环境变量中获取HOME
  40. }
  41. // 与用户进行交互,获取命令字符串
  42. int Interactive(char out[], int size)
  43. {
  44. // 输出提示符并获取用户输入的命令字符串"ls -a -l"
  45. printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());
  46. fgets(out, size, stdin);// 从标准输入读取一行
  47. out[strlen(out)-1] = 0; //'\0', commandline是空串的情况?// 移除字符串末尾的换行符'\n'
  48. return strlen(out);// 返回命令字符串的长度(不包括末尾的'\0')
  49. }
  50. // 分割用户输入的命令字符串为参数数组
  51. void Split(char in[])
  52. {
  53. int i = 0;
  54. argv[i++] = strtok(in, SEP); // "ls -a -l"// 使用空格作为分隔符分割字符串,并将第一个参数存入argv[0]
  55. while(argv[i++] = strtok(NULL, SEP)); // 故意将== 写成 =// 继续分割并存储剩余参数
  56. // 下面的代码块试图修改参数列表以在ls命令后添加"--color"
  57. if(strcmp(argv[0], "ls") ==0)
  58. {
  59. argv[i-1] = (char*)"--color";// 这会覆盖最后一个参数
  60. argv[i] = NULL;// 确保argv数组以NULL结尾
  61. }
  62. }
  63. // 执行命令
  64. void Execute()
  65. {
  66. pid_t id = fork();// 创建一个新的子进程
  67. if(id == 0)
  68. {
  69. // 在子进程中执行命令
  70. execvp(argv[0], argv);// 使用环境变量中的PATH来查找要执行的程序
  71. exit(1);// 如果execvp失败(例如找不到程序),则退出子进程并返回1
  72. }
  73. int status = 0;
  74. pid_t rid = waitpid(id, &status, 0);// 在父进程中等待子进程结束
  75. if(rid == id) lastcode = WEXITSTATUS(status); // 如果子进程正常结束,获取其退出状态并保存
  76. //printf("run done, rid: %d\n", rid);
  77. }
  78. int BuildinCmd()
  79. {
  80. int ret = 0;
  81. // 检测 argv[0] 是否为 "cd",如果是则执行 cd 命令
  82. // 1. 检测是否是内建命令, 是 1, 否 0
  83. if(strcmp("cd", argv[0]) == 0)
  84. {
  85. // 2. 执行
  86. // 标记为内建命令
  87. ret = 1;
  88. // 获取 cd 命令的参数(要切换到的目录),如果没有参数则默认为家目录
  89. char *target = argv[1]; //cd XXX or cd
  90. if(!target) target = Home();// 如果没有指定目录,则切换到用户家目录
  91. // 切换到目标目录
  92. chdir(target);
  93. // 获取当前工作目录并保存到 temp 变量中
  94. char temp[1024];
  95. getcwd(temp, 1024);
  96. // 构造新的环境变量 "PWD",并将其设置为当前工作目录
  97. snprintf(pwd, SIZE, "PWD=%s", temp);
  98. putenv(pwd);
  99. }
  100. // 检测 argv[0] 是否为 "export",如果是则执行 export 命令
  101. else if(strcmp("export", argv[0]) == 0)
  102. {
  103. ret = 1;
  104. // 如果有参数,则将其设置为环境变量
  105. if(argv[1])
  106. {
  107. strcpy(env, argv[1]);
  108. putenv(env);
  109. }
  110. }
  111. // 检测 argv[0] 是否为 "echo",如果是则执行 echo 命令
  112. else if(strcmp("echo", argv[0]) == 0)
  113. {
  114. ret = 1;
  115. // 如果没有参数,则输出一个换行符
  116. if(argv[1] == NULL) {
  117. printf("\n");
  118. }
  119. else{
  120. // 如果参数以 '$' 开头,则进行特殊处理
  121. if(argv[1][0] == '$')
  122. {
  123. // 如果参数是 "$?",则输出上一个命令的退出状态
  124. if(argv[1][1] == '?')
  125. {
  126. printf("%d\n", lastcode);
  127. lastcode = 0;
  128. }
  129. else{
  130. // 否则,获取环境变量并输出其值
  131. char *e = getenv(argv[1]+1);
  132. if(e) printf("%s\n", e);
  133. }
  134. }
  135. else{
  136. // 如果参数不是以 '$' 开头,则直接输出该参数
  137. printf("%s\n", argv[1]);
  138. }
  139. }
  140. }
  141. return ret;
  142. }
  143. int main()
  144. {
  145. while(1)
  146. {
  147. char commandline[SIZE];
  148. // 1. 打印命令行提示符,获取用户输入的命令字符串
  149. int n = Interactive(commandline, SIZE);
  150. if(n == 0) continue;
  151. // 2. 对命令行字符串进行切割
  152. Split(commandline);
  153. // 3. 处理内建命令
  154. n = BuildinCmd();
  155. if(n) continue;
  156. // 4. 执行这个命令
  157. Execute();
  158. }
  159. // for(int i=0; argv[i]; i++)
  160. // {
  161. // printf("argv[%d]: %s\n", i, argv[i]);
  162. // }
  163. return 0;
  164. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/588292
推荐阅读
相关标签
  

闽ICP备14008679号