Linux——自定义简单shell

news2024/12/27 9:24:10

shell

  • 自定义shell
    • 目标
      • 普通命令和内建命令(补充)
    • shell实现
      • 实现原理
      • 实现代码

自定义shell

目标

  • 能处理普通命令
  • 能处理内建命令
  • 要能帮助我们理解内建命令/本地变量/环境变量这些概念
  • 理解shell的运行

普通命令和内建命令(补充)

在Linux系统中,命令大致可以分为普通命令(通常指的是外部命令)和内建命令两大类。以下是关于这两类命令的详细解释:

一、普通命令(外部命令)

定义:普通命令,也被称为外部命令,是Linux系统中的实用程序部分。这些命令通常以单独的程序文件形式存在,并存储在系统的特定目录中(如/bin、/usr/bin、/sbin、/usr/sbin等)。

特点
功能强大:由于外部命令通常是独立的程序,因此它们可以包含复杂的功能和算法。

不随系统一起加载:在系统启动时,外部命令并不会被加载到内存中。它们只在被需要时,由shell程序通过PATH环境变量查找并加载到内存中执行。

执行速度相对较慢:由于需要查找和加载程序文件,以及创建子进程来执行命令,因此外部命令的执行速度通常比内建命令慢。
示例:常见的外部命令包括ls(列出目录内容)、vi(文本编辑器)、grep(文本搜索工具)等。

二、内建命令

定义:内建命令是shell程序的一部分,这些命令被直接集成在shell的源代码中,并随着shell程序的启动而被加载到内存中。

特点
执行速度快:由于内建命令是在shell程序内部执行的,因此它们不需要创建子进程或查找外部程序文件,从而提高了执行速度。

与shell紧密集成:内建命令与shell程序紧密集成,因此它们可以更方便地访问和操作shell的环境变量、函数等。

占用内存较少(但相对外部命令而言):虽然内建命令的执行速度更快,但它们通常会占用一定的内存空间。然而,由于这些命令是shell程序的一部分,因此它们所占用的内存空间通常是可以接受的。

示例:常见的内建命令包括cd(切换目录)、pwd(显示当前工作目录)、echo(输出字符串到标准输出)、history(显示命令历史记录)等。

三、区分方法

在Linux中,可以使用type命令来区分一个命令是内建命令还是外部命令。例如:

输入type cd,输出结果为cd is a shell builtin,表示cd是一个内建命令。
输入type ls,输出结果为ls is aliased to ‘ls --color=auto’(或类似信息),表示ls是一个外部命令(尽管这里显示了别名信息,但可以通过进一步使用type -t ls或查看其路径来确认其为外部命令)。

shell实现

实现原理

⽤下图的时间轴来表⽰事件的发⽣次序。其中时间从左向右。shell由标识为sh的⽅块代表,它随着时间的流逝从左向右移动。shell从⽤户读⼊字符串"ls"。shell建⽴⼀个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。
在这里插入图片描述

Shell的作用流程

  • 读取输入:
    Shell等待用户输入命令。这可以是直接在命令行提示符下输入的命令,也可以是来自脚本文件的命令。
  • 解析命令:
    Shell解析用户输入的命令,包括命令本身、参数和选项。它会检查命令的语法是否正确,以及命令是否存在。
  • 查找命令:
    Shell判断命令是内置命令还是外部命令。如果是内置命令,则直接执行;如果是外部命令,则在PATH环境变量中查找相应的程序文件。
  • 执行命令:
    Shell将解析后的命令传递给操作系统内核执行。内核会读取命令,并执行相应的操作。这包括调用系统资源、管理文件、执行程序等。
  • 返回输出:
    当命令执行完成后,Shell会返回执行结果或错误信息给用户。这可以是命令的输出内容、状态码等。
  • 环境变量:
    Shell使用环境变量来存储系统配置和用户信息。这些变量在命令执行和脚本编写中起着重要作用。例如,PATH变量指定了系统在哪些目录下查找可执行文件。
  • 重定向和管道:
    Shell支持将命令的输入和输出重定向到文件或其他设备上。同时,它还支持管道操作,可以将一个命令的输出作为下一个命令的输入。

实现代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
using namespace std;
const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
// 全局的命令⾏参数表

char *gargv[argvnum];
int gargc = 0;
// 全局的变量

int lastcode = 0;
// 我的系统的环境变量

char *genv[envnum];
// 全局的当前shell⼯作路径
 
char pwd[basesize];
char pwdenv[basesize];

string GetUserName()
{
 	string name = getenv("USER");
	return name.empty() ? "None" : name;
}
string GetHostName()
{
	 string hostname = getenv("HOSTNAME");
	 return hostname.empty() ? "None" : hostname;
}
string GetPwd()
{
	if(nullptr == getcwd(pwd, sizeof(pwd))) return "None";
 	snprintf(pwdenv, sizeof(pwdenv),"PWD=%s", pwd);
 	 putenv(pwdenv); // PWD=XXX
	 return pwd;
	 //string pwd = getenv("PWD");
	 //return pwd.empty() ? "None" : pwd;
}
string LastDir()
{
	string curr = GetPwd();
	if(curr == "/" || curr == "None") return curr;
	// /home/whb/XXX
	size_t pos = curr.rfind("/");
	if(pos == std::string::npos) return curr;
	return curr.substr(pos+1);
}

string MakeCommandLine()
{
	// [whb@bite-alicloud myshell]$ 
	char command_line[basesize];
	snprintf(command_line, basesize, "[%s@%s %s]# ",\
	GetUserName().c_str(), GetHostName().c_str(),LastDir().c_str());
	return command_line;
}
void PrintCommandLine() // 1. 命令⾏提⽰符
{
	printf("%s", MakeCommandLine().c_str());
	fflush(stdout);
}

bool GetCommandLine(char command_buffer[], int size)   //2. 获取⽤⼾命令
{
	 // 我们认为:我们要将⽤⼾输⼊的命令⾏,当做⼀个完整的字符串
	 
	// "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;
}

void ParseCommandLine(char command_buffer[], int len) // 3. 分析命令
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    // "ls -a -l -n"
    const char *sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);
    // =是刻意写的
 
    while((bool)(gargv[gargc++] = strtok(nullptr, sep)));
    gargc--;
 }
void debug()
{
   printf("argc: %d\n", gargc);
   for(int i = 0; gargv[i]; i++)
   {
       printf("argv[%d]: %s\n", i, gargv[i]);
   }
}
bool ExecuteCommand()   // 4. 执⾏命令
{
    // 让⼦进程进⾏执⾏
 
    pid_t id = fork();
    if(id < 0) return false;
    if(id == 0)
    {
        //⼦进程
        // 1. 执⾏命令
        execvpe(gargv[0], gargv, genv);
        // 2. 退出
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        if(WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else
        { 
        	lastcode = 100;
        }
        return true;
    }
    return false;
}
 
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 $PATH
            // echo hello
            if(gargv[1][0] == '$')
            {
                if(gargv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
            }
            else
            {
                printf("%s\n", gargv[1]);
                lastcode = 0;
            }
        }
        else
        {
            lastcode = 3;
        }
        return true;
    }
    return false;
}
void InitEnv()
 {
    extern char **environ;
    int index = 0;
	 while(environ[index])
	 {
		 genv[index] = (char*)malloc(strlen(environ[index])+1);
		 strncpy(genv[index], environ[index], strlen(environ[index])+1);
		 index++;
	 }
	 genv[index] = nullptr;
 }
 int main()
 {
	 InitEnv();
	 char command_buffer[basesize];
	 while(true)
	 {
		 PrintCommandLine(); // 1. 	命令⾏提⽰符
		 
		// command_buffer -> output
		 if( !GetCommandLine(command_buffer, basesize) )   // 2. 获取⽤⼾命令
		 {
		 	continue;
		 }
		 //printf("%s\n", command_buffer);
		 ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令
		 
		if ( CheckAndExecBuiltCommand() )
		{
		 	continue;
		}
		ExecuteCommand();   // 4. 执⾏命令
	}
	 return 0;
 }

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

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

相关文章

Vue进阶之单组件开发与组件通信

书接上篇&#xff0c;我们了解了如何快速创建一个脚手架&#xff0c;现在我们来学习如何基于vite创建属于自己的脚手架。在创建一个新的组件时&#xff0c;要在新建文件夹中打开终端创建一个基本的脚手架&#xff0c;可在脚手架中原有的文件中修改或在相应路径重新创建&#xf…

Webman中实现定时任务

文章目录 Webman中实现定时任务一、引言二、安装与配置1、安装Crontab组件2、创建进程文件3、配置进程文件随Webman启动4、重启Webman5、Cron表达式&#xff08;补充&#xff09;例子 三、使用示例四、总结 Webman中实现定时任务 一、引言 在现代的后端开发中&#xff0c;定时…

Android笔记(三十四):封装带省略号图标结尾的TextView

背景 项目需求需要实现在文本末尾显示一个icon&#xff0c;如果文本很长时则在省略号后面显示icon&#xff0c;使用TextView自带的drawableEnd可以实现&#xff0c;但是如果文本换行了则会显示在TextView垂直居中的位置&#xff0c;不满足要求&#xff0c;于是有了本篇的自定义…

多线程篇-8--线程安全(死锁,常用保障安全的方法,安全容器,原子类,Fork/Join框架等)

1、线程安全和不安全定义 &#xff08;1&#xff09;、线程安全 线程安全是指一个类或方法在被多个线程访问的情况下可以正确得到结果&#xff0c;不会出现数据不一致或其他错误行为。 线程安全的条件 1、原子性&#xff08;Atomicity&#xff09; 多个操作要么全部完成&a…

Day1 生信新手笔记

生信新手笔记 生信学习第一天笔记打卡。 转录组学中&#xff1a; 上游分析-基于linux&#xff0c;包括质控、过滤、比对、定量&#xff1b; 下游分析-基于R语言&#xff0c;包括差异分析、富集分析、可视化。 1. 级别标题 一个井号加空格 就是一级标题&#xff0c;两个井号加…

Git远程仓库操作

文章目录 远程仓库连接Gitee克隆代码 多人协同问题说明 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Git专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月1日13点10分 远程仓库 Git 是分布式版本控制系统&#xff0c;同一个 Git …

virtualbox给Ubuntu22创建共享文件夹

1.在windows上的操作&#xff0c;创建共享文件夹Share 2.Ubuntu22上的操作&#xff0c;创建共享文件夹LinuxShare 3.在virtualbox虚拟机设置里&#xff0c;设置共享文件夹 共享文件夹路径&#xff1a;选择Windows系统中你需要共享的文件夹 共享文件夹名称&#xff1a;挂载至wi…

人工智能-深度学习-BP算法

BP算法的核心思想是通过计算损失函数对网络参数的梯度&#xff0c;然后使用梯度下降法来更新网络参数&#xff0c;从而最小化损失函数。 误差反向传播算法(BP)的基本步骤: 前向传播&#xff1a;正向计算得到预测值。 计算损失&#xff1a;通过损失函数计算预测值和真实值的差…

(免费送源码)计算机毕业设计原创定制:Apache+JSP+Ajax+Springboot+MySQL Springboot自习室在线预约系统

摘 要 远程预约是一种全新的网络租用方式&#xff0c;它通过互联网突破了时间和空间限制&#xff0c;实现了便捷快速的预约与管理功能。在对数据信息有效组织并整合了一定使用功能后&#xff0c;远程预约系统可以方便地实现预约与取消&#xff0c;以及信息查询等功能。经过本人…

【51单片机】程序实验910.直流电机-步进电机

主要参考学习资料&#xff1a;B站【普中官方】51单片机手把手教学视频 前置知识&#xff1a;C语言 单片机套装&#xff1a;普中STC51单片机开发板A4标准版套餐7 码字不易&#xff0c;求点赞收藏加关注(•ω•̥) 有问题欢迎评论区讨论~ 目录 程序实验9&10.直流电机-步进电机…

windows 应用 UI 自动化实战

UI 自动化技术架构选型 UI 自动化是软件测试过程中的重要一环&#xff0c;网络上也有很多 UI 自动化相关的知识或资料&#xff0c;具体到 windows 端的 UI 自动化&#xff0c;我们需要从以下几个方面考虑&#xff1a; 开发语言 毋庸置疑&#xff0c;在 UI 自动化测试领域&am…

我不是挂王-用python实现燕双鹰小游戏

一.准备工作 1.前言提要 作为程序员在浩瀚的数字宇宙中&#xff0c;常常感觉现实世界是一台精密运作的虚拟机&#xff0c;其底层的物理逻辑如同铁律般难以撼动。然而我们拥有在虚拟世界中自由驰骋、创造无限可能的独特力量。突发奇我想用Python写出燕双鹰的小游戏,这样想想就很…

会议直击|美格智能亮相2024紫光展锐全球合作伙伴大会,融合5G+AI共拓全球市场

11月26日&#xff0c;2024紫光展锐全球合作伙伴大会在上海举办&#xff0c;作为紫光展锐年度盛会&#xff0c;吸引来自全球的众多合作伙伴和行业专家、学者共同参与。美格智能与紫光展锐竭诚合作多年&#xff0c;共同面向5G、AI和卫星通信为代表的前沿科技&#xff0c;聚焦技术…

3. STM32_串口

数据通信的基础概念 什么是串行/并行通信&#xff1a; 串行通信就是数据逐位按顺序依次传输 并行通信就是数据各位通过多条线同时传输。 什么是单工/半双工/全双工通信&#xff1a; 单工通信&#xff1a;数据只能沿一个方向传输 半双工通信&#xff1a;数据可以沿两个方向…

RPC与HTTP调用模式的架构差异

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;和 HTTP 调用是两种常见的通信模式&#xff0c;它们在架构上有以下一些主要差异&#xff1a; 协议层面 RPC&#xff1a;通常使用自定义的二进制协议&#xff0c;对数据进行高效的序列化和反序列化&am…

Microsoft Excel如何插入多行

1.打开要编辑的excel表&#xff0c;在指定位置&#xff0c;鼠标右键点击“插入”一行 2.按住shift键&#xff0c;鼠标的光标箭头会变化成如下图所示 3.一直按住shift键和鼠标左键&#xff0c;往下拖动&#xff0c;直至到插入足够的行

【python】图像、音频、视频等文件数据采集

【python】图像、音频、视频等文件数据采集 先安装所需要的工具一、Tesseract-OCRTesseract-OCR环境变量设置验证是否配置成功示例语言包下载失败 二、ffmpeg验证是否安装成功示例 先安装所需要的工具 一、Tesseract-OCR Tesseract是一个 由HP实验室开发 由Google维护的开源的…

虚拟机docker记录

最近看了一个up的这个视频&#xff0c;感觉docker真的挺不错的&#xff0c;遂也想来搞一下&#xff1a; https://www.bilibili.com/video/BV1QC4y1A7Xi/?spm_id_from333.337.search-card.all.click&vd_sourcef5fd730321bc0e9ca497d98869046942 这里我用的是vmware安装ubu…

C++STL之vector(超详细)

CSTL之vector 1.vector基本介绍2.vector重要接口2.1.构造函数2.2.迭代器2.3.空间2.3.1.resize2.3.2.capacity 2.4.增删查找 3.迭代器失效4.迭代器分类 &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f68…

深度学习实验十三 卷积神经网络(4)——使用预训练resnet18实现CIFAR-10分类

目录 一、数据加载 二、数据集类构建 三、模型构建 四、模型训练 五、模型评价及预测 附完整可运行代码&#xff1a; 实验大体步骤&#xff1a; 注&#xff1a; 在自己电脑的CPU跑代码 连接远程服务器跑代码√ 本次实验由于数据量巨大&#xff0c;我的笔记本上还没有…