当前位置: 首页 > news >正文

Linux进程控制(3)(进程程序替换2 -- 微型shell)

目录

补充

一.引入

 二.自助微型shell

1:输出一个命令行

2:获取用户命令字符串

3:命令行字符串的分割

4:先试着执行一下命令

5:关键点:需要在执行命令前检查是否为内建命令

其他:

三:完整代码


补充

二号手册的是系统调用,而上面的程序替换有关的函数都是三号手册,即不是系统调用

实际上是由我们Linux支持的标准GNU C语言

一.引入

前面我们写的关于程序替换的一个小代码:

进程创建子进程,然后由子进程去执行命令,1.子进程可以执行系统当中的任意一条命令,包括我们自己写的可执行程序;2.子进程在执行命令的时候,执行一次就结束了那么如果让它一直可以执行命令,那它不就是个shell吗??

 二.自助微型shell

所谓的shell,就是可以一直去执行,而且执行各种命令,所以要自定义写个shell的话,本质上就是要去做命令行字符串的解析,还要打循环不断地去创建进程执行命令

我们输入的指令其实本质就是一段字符串,需要bash去解析然后执行

如:  --- > "ls -a -l"

1:输出一个命令行

输出一个类似这个的命令行,我们就需要去取得用户名、主机名以及路径

而这些东西刚好可以从env环境变量中取得,通过getenv函数

先写获取这些字符串的接口,最后到一个Print函数中去引用这些接口得到值后再打印就好了

2:获取用户命令字符串

为什么这里printf中我只输入了一个\n换行符号,却出现的一个空行呢???

因为在输入字符串时,在最后一下按了回车键,导致fgets读取了,然后由自己加了一个\n,因此换行了两次

解决方法:把尾部的\n换成\0:(这里define了ZERO为\0

3:命令行字符串的分割

若果要以空格分隔的话,第二个参数应该传" ",而不是' ',因为其类型是char*

故意写成 = ,表示先赋值,再判断,分割完了之后,strtok就会返回NULL,刚好让gArgv最后一个元素是NULL,并且while判断结束

这个方法很巧妙

4:先试着执行一下命令

errno是一个全局变量,会记录最近一次进程执行的退出码

5:关键点:需要在执行命令前检查是否为内建命令

比如cd这个命令,如果按照原来的逻辑去执行的话,那么他只是子进程在cd,父进程却没动

因此我们又要提及到内建命令

其他:

系统的是lessen12   而我们写的是整个路径,如何改善呢??

#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)

这里SkipPath的作用是跳过前面所有的字符直到最后一个 /lessen12

三:完整代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)// 定义到一起 易观 
char cwd[SIZE*2];
char *gArgv[NUM];
int lastcode = 0;void Die()
{exit(1);
}const char *GetUserName()
{const char* name = getenv("USER");if(name == NULL) return "None";return name;
}const char *GetHostName()
{const char *hostname = getenv("HOSTNAME");if(hostname == NULL) return "Node";return hostname;
}const char *GetCwd()
{const char *cwd = getenv("PWD");if(cwd == NULL) return "None";return cwd;
}void MakeCommandLineAndPrint(char line[], size_t size)
{const char *username = GetUserName();const char *hostname = GetHostName();const char *cwd = GetCwd();SkipPath(cwd);snprintf(line, size, "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1); printf("%s", line);fflush(stdout);
}int GetUserCommand(char command[], size_t n)
{char *s = fgets(command, n, stdin);if(s == NULL) return -1;command[strlen(command) - 1] = ZERO;return strlen(command);
}void SplitCommand(char command[], size_t n)
{(void)n;// "ls -a -l -n" -> "ls" "-a" "-l" "-n"gArgv[0] = strtok(command, SEP);int index = 1;while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意写成=,表示先赋值,在判断. 分割之后,strtok会返回NULL,刚好让gArgv最后一个元素是NULL, 并且while判断结束
}void ExecuteCommand()
{pid_t id = fork();if(id < 0) Die();else if(id == 0){// childexecvp(gArgv[0], gArgv);exit(errno);}else{// fahterint status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);}}
}const char *GetHome()
{const char* home = getenv("HOME");if(home == NULL) return "/";return home;}void Cd()
{const char *path = gArgv[1];if(path == NULL) path = GetHome();// path 一定存在chdir(path);// 刷新环境变量char temp[SIZE*2];getcwd(temp, sizeof(temp));snprintf(cwd, sizeof(cwd), "PWD=%s", temp);putenv(cwd);
}int CheckBuildin()
{int yes = 0;const char *enter_cmd = gArgv[0];if(strcmp(enter_cmd, "cd") == 0){yes = 1;Cd();}else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0){yes = 1;printf("%d\n", lastcode);lastcode = 0;}return yes;
}int main()
{int quit = 0;while(!quit){// 1.输出一个命令行char commandline[SIZE];MakeCommandLineAndPrint(commandline, sizeof(commandline));// 2.获取用户命令字符串char usercommand[SIZE];int n = GetUserCommand(usercommand, sizeof(usercommand));// 3.命令行字符串的分割SplitCommand(usercommand, sizeof(usercommand)); // 4. 检测命令是否是内建命令n = CheckBuildin();if(n) continue;// n.执行命令ExecuteCommand();printf("echo : %s\n", usercommand);}return 0;
}

http://www.mrgr.cn/news/49188.html

相关文章:

  • PyTorch gather与scatter_详解
  • 视频格式转换
  • Spring Boot知识管理:智能搜索与分析
  • 常用STL容器(c++)
  • 【畅捷通-注册安全分析报告】
  • 【python】内置装饰器-@property
  • unordered_set、unordered_multiset、unordered_map、unordered_multimap的介绍及使用
  • gitlab的基本用法之创建用户和组
  • 美国的社会分裂延续至AI领域
  • 02.07.链表相交 最简方法之一
  • 【解锁AI潜能:如何通过精确Prompt撰写引导智能对话】
  • JavaWeb 18.过滤器
  • 一文通透OpenAI o1:从CoT、Self-Correct/STaR、Self-play RL、MCST等技术细节到工程复现
  • 一些硬件知识【20241013】
  • 股市投资,如何应对人性挑战与把握关键策略?
  • 【JavaScript】Array的unshift的实现
  • 文件和目录的权限管理
  • Kaggle竞赛——森林覆盖类型分类
  • Android开发首页底部tab切换图标有动画效果
  • Linux——DNS服务器正向解析搭建教程