【Linux】模拟Shell命令行解释器

一、知识补充

1.1 snprintf

snprintf() 是 C语言的一个标准库函数,定义在<stdio.h>头文件中。

snprintf() 函数的功能是格式化字符串,并将结果存储在指定的字符数组中。该函数的原型如下:

int snprintf(char *str, size_t size, const char *format[,argument...]);

参数

  • str:指向一个字符数组,用于存储格式化后的字符串,该数组的大小至少为 size。
  • size:指定写入 str 数组中字符的最大个数(包括最后的空字符 '\0')。
  • format:包含格式说明符的字符串,它定义了后续参数的输出格式。
  • [,argument...]:可变参数列表,与格式字符串中的格式说明符相匹配

return 

  • 如果参数 size 的值足够大,则函数返回写入到 str 数组中的字符个数(不包括结尾的空字符),它的值位于[0, size-1]之间。
  • 如果出现编码错误,则返回一个负数。
  • 请注意,只有当这个返回值是非负且小于n时,字符串才被完整地写入了。

1.2 fflush 

fflush()函数:更新缓存区。头文件:#include<stdio.h> 

调用fflush()会将缓冲区中的内容写到stream所指的文件中去.若stream为NULL,则会将所有打开的文件进行数据更新。 

int fflush(FILE *stream);

fflush(stdin):刷新缓冲区,将缓冲区内的数据清空并丢弃。
fflush(stdout):刷新缓冲区,将缓冲区内的数据输出到设备。

  1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {5     printf("hello");                                                                                                            6 7     sleep(5);8 9     printf(" world!\n");10 11     return 0;12 }

5秒后打印hello world!

  1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {5     printf("hello\n");                                                                                                            6 7     sleep(5);8 9     printf(" world!\n");10 11     return 0;12 }

 先打印hello,5秒后打印 world!\n有刷新功能

  1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {                                                                                                                               5     printf("hello");6 7     fflush(stdout);//将缓冲区的内容输出到设备中8 9     sleep(5);10 11     printf(" world!\n");12 13     return 0;14 }

先打印hello5秒后打印 world!

fflush()的作用是用来刷新缓冲区,fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃; fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上。

 1.3 fgets

fgets是C标准库中用于从文件或标准输入流中读取一行字符的函数,常用于处理字符串输入。它的主要作用是读取文件或标准输入中的一行,直到遇到换行符\n或达到指定的字符数为止。

char * fgets ( char * str, int num, FILE * stream );

参数

  • str:这是一个指向字符数组的指针,fgets将把读取的字符存储到这个数组中。
  • num:这是一个整数,表示最多读取的字符数(包括\0终止符)。即使没有读取到换行符,fgets也会在读取的字符数达到num-1时停止
  • stream:这是输入流,可以是文件流(如stdin、stdout)或者其他文件指针。

返回值:

  • 如果读取成功,fgets返回str,即指向读取数据的字符数组。
  • 如果发生错误或达到文件末尾,fgets返回NULL。

1.4 strtok

C语言字符函数和字符串函数-CSDN博客

可参考第11节中的strtok

1.5 getcwd

 getcwd是属于系统接口

#include <unistd.h>
char *getcwd(char *buf, size_t size);

getcwd()会将当前工作目录的绝对路径复制到参数buf所指的内存空间中,参数size为buf的空间大小。

如果getcwd函数执行失败,它将返回NULL

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {char cwd[1024];if (getcwd(cwd, sizeof(cwd))!= NULL) {printf("当前工作目录是: %s\n", cwd);} else {perror("获取当前工作目录出错");return 1;}return 0;
}

像pwd就是调用getcwd这个系统接口 

[zxw@hcss-ecs-cc58 myshell]$ pwd
/home/zxw/linux/112/lesson16/myshell

1.6 chdir 

chdir属于是系统接口

#include <unistd.h>
int chdir(const char *path//路径);

用于改变当前工作目录,其参数为Path 目标目录,可以是绝对目录或相对目录。

成功返回0,错误返回-1。

1.7 putenv

#include <stdlib.h>
int putenv(char *string);

函数说明:putenv()用来改变或增加环境变量的内容.

参数string 的格式为name=value, 如果该环境变量原先存在, 则变量内容会依 value 改变, 否则此参数内容会成为新的环境变量。

返回值:执行成功则返回0, 有错误发生则返回-1. 

 二、模拟Shell命令行解释器

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>using namespace std;const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
// 全局的命令行参数表
char *gargv[argvnum];
int gargc = 0;using namespace std;
// 我系统的环境变量表
char *genv[envnum];// 全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];// 全局变量
int lastcode = 0;string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}string GetHostName()
{string hostname = getenv("HOSTNAME");return hostname.empty() ? "None" : hostname;
}string GetPwd()
{string pwd = getenv("PWD");return pwd.empty() ? "None" : pwd;// string pwd = getenv("PWD");// 获取当前pwdif(nullptr == getcwd(pwd,sizeof(pwd))) return "None";// 修改环境变量snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);putenv(pwdenv);return pwd;
}string LastDir()
{string curr =GetPwd();if(curr == "/" || curr == "None") return curr;size_t pos = curr.rfind("/");if(pos == string::npos) return curr;return curr.substr(pos+1);
}// 1.命令行提示符
string MakeCommandLine()
{char command_line[basesize];snprintf(command_line,basesize,"[%s@%s %s]# ",GetUserName().c_str(),GetHostName().c_str(),GetPwd().c_str());GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());return command_line;
}void PrintCommandLine()
{printf("%s",MakeCommandLine().c_str());fflush(stdout);
}void debug()
{printf("argc:%d\n",gargc);for(int i = 0; gargv[i]; i++){printf("argv[%d]:%s\n",i,gargv[i]);}
}bool GetCommandLine(char command_buffer[],int size)// 2.获取用户命令
// 2.获取用户命令
bool GetCommandLine(char command_buffer[],int size)
{// 我们认为,我们要将用户输入的命令行,当成一个完整的字符串// "ls -a -l -n"char *result = fgets(command_buffer,size,stdin);if(!result){return false;}command_buffer[strlen(command_buffer)-1] = 0;if(strlen(command_buffer) == 0) return false;return true;
}//3.分析命令
void ParseCommandLine(char command_buffer[],int len)
{(void)len;memset(gargv, 0, sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer,sep);// =是刻意写的 ---》最后返回NULLwhile(gargv[gargc++] = strtok(nullptr,sep)); gargc--;
}void AddEnv(const char *item)
{int index = 0;while(genv[index]){index++;}genv[index] = (char*)malloc(strlen(item)+1);strncpy(genv[index],item,strlen(item)+1);genv[++index] = nullptr;}// shell自己执行命令,本质是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{if(strcmp(gargv[0],"cd") == 0){// 内建命令没有创建子进程,自己执行if(gargc == 2){chdir(gargv[1]);lastcode = 0;}else{lastcode = 1;}return true;}else if(strcmp(gargv[0],"export") == 0){// export也是内建命令if(gargc == 2){AddEnv(gargv[1]);lastcode = 0;}else{lastcode = 2;}return true;}else if(strcmp(gargv[0],"env") == 0){for(int i = 0; genv[i]; i++){printf("%s\n",genv[i]);}lastcode = 0;return true;}else if(strcmp(gargv[0],"echo") == 0){if(gargc == 2){// echo $?// echo helloif(gargv[1][0] == '$'){if(gargv[1][1] == '?'){printf("%d\n",lastcode);lastcode = 0;}}else{printf("%s\n",gargv[1]);}}else{lastcode = 3;}return true;}return false;
}// 4.执行命令
// 在shell中
// 有些命令,必须由子进程来执行
// 有些命令,不能由子进程执行,要由shell自己执行bool ExecuteCommand()
{// 让子进程进行执行pid_t id = fork();if(id < 0){return false;}if(id == 0){//child//1.执行命令execvp(gargv[0],gargv);execvpe(gargv[0],gargv,genv);//2.退出exit(1);}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid < 0)if(rid > 0){//DO Nothingif(WIFEXITED(status)){lastcode = WEXITSTATUS(status);}else{lastcode = 100;}return true;}return false;
}// shell自己执行命令,本质是shell调用自己的函数
bool CheckAndExecBuiltCommand()
// 作为一个shell,获取环境变量应该从系统的配置
// 我们今天就直接从父进程获取环境变量
void InitEnv()
{if(strcmp(gargv[0],"cd") == 0)extern char **environ;int index = 0;while(environ[index]){// 内建命令if(gargc == 2){chdir(gargv[1]);}return true;genv[index] = (char*)malloc(strlen(environ[index])+1);strncpy(genv[index],environ[index],strlen(environ[index]+1));index++;}return false;genv[index] = nullptr;
}int main()
{InitEnv();char command_buffer[basesize];while(true){PrintCommandLine();// 1.命令行提示符// command_line -> outputif(!GetCommandLine(command_buffer, basesize))// 2.获取用户命令{continue;}// printf("%s\n",command_buffer); //"ls -a -b" ---> "ls" "-a" "-b"ParseCommandLine(command_buffer,strlen(command_buffer));// 3.分析命令//debug();//debug();//检查 if(CheckAndExecBuiltCommand()){continue;}ExecuteCommand(); // 4.执行命令}return 0;
}

通过模拟实现了解环境变量也是单独申请了一块地址的,另外我们之前所学习的本地变量也是通过一个数组来维护的。也清楚的了解一些为什么要内建命令,不能单独fork子进程。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/505495.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI多模态技术介绍:视觉语言模型(VLMs)指南

本文作者&#xff1a;AIGCmagic社区 刘一手 AI多模态全栈学习路线 在本文中&#xff0c;我们将探讨用于开发视觉语言模型&#xff08;Vision Language Models&#xff0c;以下简称VLMs&#xff09;的架构、评估策略和主流数据集&#xff0c;以及该领域的关键挑战和未来趋势。通…

IP 地址与蜜罐技术

基于IP的地址的蜜罐技术是一种主动防御策略&#xff0c;它能够通过在网络上布置的一些看似正常没问题的IP地址来吸引恶意者的注意&#xff0c;将恶意者引导到预先布置好的伪装的目标之中。 如何实现蜜罐技术 当恶意攻击者在网络中四处扫描&#xff0c;寻找可入侵的目标时&…

PLC实现HTTP协议JSON格式数据上报对接的参数配置说明

IGT-SER系列PLC通讯智能网关支持HTTP协议GET和POST、PUT请求模式。支持JSON格式的文件&#xff0c;也可以实现WebService的调用。 通常智能网关是HTTP协议的客户端&#xff0c;也可以同时作为HTTP的服务端。相关案例 作为客户端时支持触发、周期、混合等多种工…

设计模式-结构型-组合模式

1. 什么是组合模式&#xff1f; 组合模式&#xff08;Composite Pattern&#xff09; 是一种结构型设计模式&#xff0c;它允许将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性。换句话说&#xff0c;组合模式允…

30天开发操作系统 第 12 天 -- 定时器 v1.0

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单&#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器&#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…

【Linux】sed编辑器

一、基本介绍 sed编辑器也叫流编辑器&#xff08;stream editor&#xff09;&#xff0c;它是根据事先设计好得一组规则编辑数据流。 交互式文本编辑器&#xff08;如Vim&#xff09;中&#xff0c;可以用键盘命令交互式地插入、删除或替换文本数据。 sed编辑器是根据命令处理…

RK3568 Android 13 内置搜狗输入法小计

问&#xff1a;为什么写&#xff1f; 答&#xff1a;网上搜出来的都试过了&#xff0c;不行&#xff01;下面直接上代码和注意事项&#xff01; 首先到这个目录&#xff08;/RK3568/Rockchip_Android13_SDK_Release/device/rockchip/rk356x/tl3568_evm/preinstall&#xff09…

IT面试求职系列主题-人工智能(一)

想成功求职&#xff0c;必要的IT技能一样不能少&#xff0c;再从人工智能基础知识来一波吧。 1&#xff09;您对人工智能的理解是什么&#xff1f; 人工智能是计算机科学技术&#xff0c;强调创造能够模仿人类行为的智能机器。这里智能机器可以定义为能够像人一样行动、像人一…

数据挖掘实训:天气数据分析与机器学习模型构建

随着气候变化对各行各业的影响日益加剧&#xff0c;精准的天气预测已经变得尤为重要。降雨预测在日常生活中尤其关键&#xff0c;例如农业、交通和灾害预警等领域。本文将通过机器学习方法&#xff0c;利用历史天气数据预测明天是否会下雨&#xff0c;具体内容包括数据预处理、…

Figma如何装中文字体-PingFang苹方字体、Alibaba PuHuiTi阿里普惠

**写在前面&#xff1a; 工具类软件更新迭代如此快的世界&#xff0c;不能靠历史知识来做操作反应。需要着眼于当下工具的形态来思考用法。另外&#xff0c;有人说&#xff0c;当前的用户越来越少发教程类的图文消息了&#xff08;转去了视频&#xff09;&#xff0c;现在很多…

欧拉公式和傅里叶变换

注&#xff1a;英文引文机翻&#xff0c;未校。 中文引文未整理去重&#xff0c;如有异常&#xff0c;请看原文。 Euler’s Formula and Fourier Transform Posted byczxttkl October 7, 2018 Euler’s formula states that e i x cos ⁡ x i sin ⁡ x e^{ix} \cos{x} i …

docker搭建atlassian-confluence:7.2.0

文章目录 引言I 部署前准备数据库镜像准备自己构建镜像dockerhub第三方镜像II 安装启动容器基础配置(获取服务器ID)授权码获取集群选择设置数据库配置管理员账号引言 准备数据库、镜像启动容器获取服务器ID根据服务器ID等信息,基于atlassian-agent.jar 授权I 部署前准备 数…

抢占欧洲电商高地,TikTok 运营专线成 “秘密武器”

在当今数字化浪潮席卷全球的时代&#xff0c;社交媒体平台已成为商业拓展的关键阵地&#xff0c;TikTok 更是其中的闪耀新星。近日&#xff0c;一则重磅消息引发行业关注&#xff1a;TikTok 正计划于 2025 年初进军荷兰电商市场。这一战略布局&#xff0c;不仅彰显了 TikTok 对…

牛客网刷题 ——C语言初阶——BC96-有序序列判断

1. 题目描述——BC96-有序序列判断 牛客网OJ题链接 描述&#xff1a;输入一个整数序列&#xff0c;判断是否是有序序列&#xff0c;有序&#xff0c;指序列中的整数从小到大排序或者从大到小排序(相同元素也视为有序)。 示例1 输入&#xff1a; 5 1 6 9 22 30 输出&#xff…

LabVIEW软件Bug的定义与修改

在LabVIEW软件开发过程中&#xff0c;bug&#xff08;程序错误或缺陷&#xff09;指的是程序中导致不符合预期行为的任何问题。Bug可能是由于编码错误、逻辑漏洞、硬件兼容性问题、系统资源限制等因素引起的。它可能会导致程序崩溃、功能无法正常执行或输出结果不符合预期。理解…

HBuilderX打包ios保姆式教程

1、登录苹果开发者后台并登录已认证开发者账号ID Sign In - Apple 2、创建标识符&#xff08;App ID&#xff09;、证书&#xff0c;描述文件 3、首先创建标识符&#xff0c;用于新建App应用 3-1、App的话直接选择第一个App IDs&#xff0c;点击右上角继续 3-2、选择App&#x…

【PyTorch入门】使用PyTorch构建一个简单的图像分类模型

本次分享一个简单的使用PyTorch进行图像分类模型搭建的小案例&#xff0c;让大家对PyTorch的流程有一个认知。 1. 导入必要的库 import torch import torch.nn as nn import torchvision import numpy as np from torch.autograd import Variable import matplotlib.pyplot as…

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

前言 &#x1f31f;&#x1f31f;本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &am…

智慧公厕大数据驱动下的公共卫生管理与优化

在快速发展的城市化进程中&#xff0c;公共卫生问题日益凸显&#xff0c;成为城市管理的重要议题。智慧公厕&#xff0c;作为公共卫生设施的一次革命性创新&#xff0c;正借助物联网技术的东风&#xff0c;引领公共卫生进入一个全新的生态时代。本文将深入探讨智慧公厕如何利用…

Git:Cherry-Pick 的使用场景及使用流程

前面我们说了 Git合并、解决冲突、强行回退等解决方案 >> 点击查看 这里再说一下 Cherry-Pick功能&#xff0c;Cherry-Pick不是merge&#xff0c;只是把部分功能代码Cherry-Pick到远程的目标分支 git cherry-pick功能简介&#xff1a; git cherry-pick 是用来从一个分…