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

news2024/11/26 5:45:44

目录

补充

一.引入

 二.自助微型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)
    {
        // child
        execvp(gArgv[0], gArgv);
        exit(errno);
    }
    else
    {
        // fahter
        int 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.coloradmin.cn/o/2210582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JSONAJAX

JSON&&AJAX 一、JSON 1.1 json简介 JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON采用完全独立于语言的文本格式&#xff0c;而且很多语言都提供了对 json 的支持&#xff08;包括 C, C, C#, J…

常用的devops工具集成方法

常用的devops工具集成方法涵盖了软件开发和运维的各个方面&#xff0c;从版本控制到自动化构建、测试、部署和监控。这些工具的有效集成可以帮助团队提高协作效率&#xff0c;减少沟通障碍&#xff0c;实现快速、高质量的软件交付。以下是对这些工具集成方法的具体介绍&#xf…

双核DSP芯片的内存管理

总体框图 总的来说&#xff0c;在这款DSP芯片上&#xff0c;RAM分成了四种不同的类型&#xff1a; 专用RAM: CPU专用的内存&#xff0c;其他主机无法访问。 双核共享RAM: CLA和CPU共享内存&#xff0c;双方都可以对内存地址进行访问 MSGRAM&#xff1a; CLA和CPU之间传递数据的…

【自动驾驶汽车通讯协议】I2C(IIC)总线通讯技术详解

文章目录 0. 前言1. I2C简介2.I2C的工作原理2.1 硬件要求&#xff1a;2.2 半双工通信&#xff1a; 3. 通信时序4. 其他特性4.1 通信速率4.2 抗干扰措施4.3 注意事项 5. 在自动驾驶汽车中的应用5.1 I2C操作模式5.2 I2C的用途 6. 总结 0. 前言 按照国际惯例&#xff0c;首先声明&…

Spring Boot知识管理系统:创新与实践

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

【含开题报告+文档+PPT+源码】基于人脸识别的课堂考勤系统的设计与实现

开题报告 随着科技的不断发展&#xff0c;人脸识别技术已经逐渐渗透到各个领域&#xff0c;包括教育领域。传统的课堂考勤方式通常依赖于学生签到或教师手动记录&#xff0c;这种方式存在着许多不足之处&#xff0c;例如容易出现人为错误、耗费时间和资源等。为了解决这些问题…

机器学习:强化学习中的探索策略全解析

引言 在机器学习的广阔领域中&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&#xff09;无疑是一个充满魅力的子领域。它通过智能体与环境的交互&#xff0c;学习如何在特定的任务中做出最优决策。然而&#xff0c;在这个过程中&#xff0c;探索&#xff08;ex…

linux 配置ssh免密登录

一、 cd /root/.ssh/ #不存在就创建mkdir /root/.ssh ssh-keygen #连续按4个回车 ll二、将公钥发送到目标服务器下 #公钥上传到目标服务器 ssh-copy-id root192.168.31.142 #回车完也是要输入密码的 #测试一下免密登录&#xff1a; ssh root192.168.31.142 成功

二、后台管理系统布局菜单可拖动

前两天产品提出了一个需求&#xff0c;说后台管理系统的左边菜单的名称字数过多&#xff0c;遮挡了。希望能让客户能够看到全部的名称&#xff0c;给左侧菜单增加一个可拖动的功能&#xff0c;经过我的研究&#xff0c;这个功能最终也做出来了&#xff0c;先看效果&#xff0c;…

IDEA之手动添加作者信息

IDEA不能像eclipse一样&#xff0c;直接使用/**注释快捷键在类上带出作者信息。 解决方案&#xff1a; 手动添加自定义作者注释快捷键 /*** Author: TroubleBoy* Date: $DATE$* Description: TODO*/ 6.编辑日期变量&#xff0c;设置变量DATE的Expression为date() 7.应用Java…

Nacos的相关资料

目录 Nacos的Mysql文件&#xff1a; custom.env文件&#xff1a; 执行的Docker命令&#xff1a; 从 GitHub 官方仓库下载 Nacos&#xff1a;https://github.com/alibaba/nacos Nacos的Mysql文件&#xff1a; -- --------------------------------------------------------…

【工具类】hutool http请求获取S3图片流

博主介绍&#xff1a;✌全网粉丝20W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…

基于数字信号处理的语音数字识别器

基于数字信号处理的语音数字识别器 系统说明 该系统工作流程图如下 各部分关键参数及简要说明如下。 预处理阶段&#xff1a; 预录制语音为单个数字的语音&#xff0c;从 0 - 9 共 10 组&#xff0c;采样率 16 kHz计算 MFCC 特征中&#xff0c;Mel 滤波器组个数 40&#xff…

Journey Training:o1的一次复现尝试,极长思维链的合成

知乎&#xff1a;啦啦啦啦&#xff08;已授权&#xff09;链接&#xff1a;https://zhuanlan.zhihu.com/p/902522340 论文&#xff1a;O1 Replication Journey: A Strategic Progress Report链接&#xff1a;https://github.com/GAIR-NLP/O1-Journey 这篇论文记录了一次o1复现尝…

IBMS集成管理平台——推动建筑业数字化转型

随着城市发展和科技进步&#xff0c;智慧楼宇(IBMS)作为建筑智能化的核心系统&#xff0c;已经成为现代建筑中不可或缺的一部分。通过整合各种智能设备和系统&#xff0c;IBMS提供了对建筑内部运行状况的全面监控和管理&#xff0c;实现了能源节约、安全保障和舒适体验的多重目…

关于拖拽时需要注意的细节

cklick,mousedown,mouseup 当触发点击事件后三者的触发顺序 当元素放大用scalse时&#xff0c;检测鼠标的移动距离用不用/scale&#xff1f; <!doctype html> <html lang"en"><head><meta charset"UTF-8" /><link rel"ic…

【专题】智启未来:新质生产力引擎驱动下的智能制造行业革新报告合集PDF分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37856 在当今全球经济格局深刻变革的大背景下&#xff0c;制造业作为国家经济的基石&#xff0c;正处在高质量发展的关键历史时期。智能决策作为一股崭新的力量&#xff0c;正逐步成为推动制造业数智化转型的强大新动能。众多制造企…

C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式

今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法&#xff0c;来回顾一些基础知识点。 01第一类、字符串方式 这类方法是通过string类型自身方法直接实现。 1、Substring方法 相信大多数人第一个想到的可能就是这个方法。Substring方法是字符串内置方法&#…

基于苔藓生长优化算法(Moss Growth Optimization, MGO)的多无人机协同三维路径规划(提供MATLAB代码)

一、苔藓生长优化算法介绍 苔藓生长优化算法&#xff08;Moss Growth Optimization, MGO&#xff09;是一种受自然界苔藓生长机制启发的元启发式优化算法。这种算法模仿了苔藓在潮湿环境中的扩散和生长方式&#xff0c;以寻找复杂问题的最优解。以下是苔藓生长优化算法的一些基…

如何清空回收站后在 Windows 11/10 中恢复已删除的文件

这篇文章将解释如何将已删除的文件、文件夹和其他项目从回收站还原或恢复到原始位置。有时&#xff0c;我们最终会删除重要的文件和文件夹&#xff0c;然后我们不知道如何将它们恢复到原来的位置。但是您不必担心&#xff0c;因为这篇针对初学者的帖子将详细指导您完成所有步骤…