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

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

        1. 获取命令行
        2. 解析命令行
        3. 建立一个子进程(fork)
        4. 替换子进程(execvp)
        5. 父进程等待子进程退出(wait)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


#define SIZE 1024
#define MAX_ARGC 64
#define SEP " " // 命令参数之间的分隔符

// 全局变量    
char *argv[MAX_ARGC]; // 用于存储解析后的命令行参数
char pwd[SIZE]; // 当前工作目录
char env[SIZE]; // for test
int lastcode = 0; // 最后一个子进程的退出状态

// 获取主机名 
const char* HostName()
{
    char *hostname = getenv("HOSTNAME"); // 从环境变量中获取HOSTNAME 
    if(hostname) return hostname; // 如果找到,返回主机名
    else return "None";// 否则返回"None" 
}

// 获取用户名 
const char* UserName()
{
    char *username = getenv("USER");
    if(username) return username; // 如果找到,返回用户名
    else return "None"; // 否则返回"None"
}

// 获取当前工作目录
const char *CurrentWorkDir()
{
    char *pwd = getenv("PWD"); // 应该是从环境变量中获取PWD 
    if(pwd) return pwd; // 如果找到,返回当前工作目录 
    else return "None"; // 否则返回"None" 
}

// 获取用户主目录  
char *Home()
{
    return getenv("HOME"); // 从环境变量中获取HOME 
}

// 与用户进行交互,获取命令字符串 
int Interactive(char out[], int size)
{
    // 输出提示符并获取用户输入的命令字符串"ls -a -l"
    printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());
    fgets(out, size, stdin);// 从标准输入读取一行
    out[strlen(out)-1] = 0; //'\0', commandline是空串的情况?// 移除字符串末尾的换行符'\n' 
    return strlen(out);// 返回命令字符串的长度(不包括末尾的'\0')
}

// 分割用户输入的命令字符串为参数数组 
void Split(char in[])
{
    int i = 0; 
    argv[i++] = strtok(in, SEP); // "ls -a -l"// 使用空格作为分隔符分割字符串,并将第一个参数存入argv[0] 
    while(argv[i++] = strtok(NULL, SEP)); // 故意将== 写成 =// 继续分割并存储剩余参数
    // 下面的代码块试图修改参数列表以在ls命令后添加"--color"
    if(strcmp(argv[0], "ls") ==0)
    {
        argv[i-1] = (char*)"--color";// 这会覆盖最后一个参数
        argv[i] = NULL;// 确保argv数组以NULL结尾 
    }
}

// 执行命令  
void Execute()
{
    pid_t id = fork();// 创建一个新的子进程
    if(id == 0)
    {
        // 在子进程中执行命令 
        execvp(argv[0], argv);// 使用环境变量中的PATH来查找要执行的程序  
        exit(1);// 如果execvp失败(例如找不到程序),则退出子进程并返回1  
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);// 在父进程中等待子进程结束
    if(rid == id) lastcode = WEXITSTATUS(status); // 如果子进程正常结束,获取其退出状态并保存
    //printf("run done, rid: %d\n", rid);
}

int BuildinCmd()
{
    int ret = 0;
    // 检测 argv[0] 是否为 "cd",如果是则执行 cd 命令 
    // 1. 检测是否是内建命令, 是 1, 否 0
    if(strcmp("cd", argv[0]) == 0)
    {
        // 2. 执行
        // 标记为内建命令 
        ret = 1;
        // 获取 cd 命令的参数(要切换到的目录),如果没有参数则默认为家目录  
        char *target = argv[1]; //cd XXX or cd
        if(!target) target = Home();// 如果没有指定目录,则切换到用户家目录
         // 切换到目标目录
        chdir(target);
        // 获取当前工作目录并保存到 temp 变量中
        char temp[1024];
        getcwd(temp, 1024);
        // 构造新的环境变量 "PWD",并将其设置为当前工作目录 
        snprintf(pwd, SIZE, "PWD=%s", temp);
        putenv(pwd);
    }
    // 检测 argv[0] 是否为 "export",如果是则执行 export 命令
    else if(strcmp("export", argv[0]) == 0)
    {
        ret = 1;
        // 如果有参数,则将其设置为环境变量
        if(argv[1])
        {
            strcpy(env, argv[1]);
            putenv(env);
        }
    }
    // 检测 argv[0] 是否为 "echo",如果是则执行 echo 命令 
    else if(strcmp("echo", argv[0]) == 0)
    {
        ret = 1;
        // 如果没有参数,则输出一个换行符 
        if(argv[1] == NULL) {
            printf("\n");
        }
        else{
            // 如果参数以 '$' 开头,则进行特殊处理 
            if(argv[1][0] == '$')
            {
                // 如果参数是 "$?",则输出上一个命令的退出状态
                if(argv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
                else{
                    // 否则,获取环境变量并输出其值 
                    char *e = getenv(argv[1]+1);
                    if(e) printf("%s\n", e);
                }
            }
            else{
                 // 如果参数不是以 '$' 开头,则直接输出该参数 
                printf("%s\n", argv[1]);
            }
        }
    }
    return ret;
}

int main()
{
    while(1)
    {
        char commandline[SIZE];
        // 1. 打印命令行提示符,获取用户输入的命令字符串
        int n = Interactive(commandline, SIZE);
        if(n == 0) continue;
        // 2. 对命令行字符串进行切割
        Split(commandline);
        // 3. 处理内建命令
        n = BuildinCmd();
        if(n) continue;
        // 4. 执行这个命令
        Execute();
    }
   // for(int i=0; argv[i]; i++)
   // {
   //     printf("argv[%d]: %s\n", i, argv[i]);
   // }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iZotope RX 11 for Mac:音频修复的终极利器

在音频制作的浩瀚星海中&#xff0c;每一份声音都是珍贵的宝石&#xff0c;但往往被各种噪音、杂音所掩盖。此刻&#xff0c;iZotope RX 11 for Mac犹如一位专业的匠人&#xff0c;以其精湛的技术&#xff0c;将每一份声音雕琢至完美。 iZotope RX 11 for Mac&#xff0c;这是…

【源码】二开 欧亚版自动抢单系统源码(中、英、泰、日四语言)

二开 欧亚版自动抢单系统源码(中、英、泰、日四语言) 这个版本除了前台界面做了一些改变&#xff0c;同时功能细节上也做了一些改动。 如增加订单、充值、提现假人管理&#xff1b;增加短信开关&#xff0c;增加短信宝、阿里云、云之讯短信接口&#xff1b;增加商品匹配区间设…

男士内裤有什么牌子?2024年公认男士内裤口碑最好的品牌

对于男性来说&#xff0c;一条内裤都是可以穿好久的存在。但实际上作为贴身衣物的内裤&#xff0c;最好是3-6月就更换一次是最好的&#xff0c;如果长期不更换会使内裤的舒适性透气性降低&#xff0c;而且残留细菌无法得到清除&#xff01; 面对市面上琳琅满目的品牌和材质分类…

vscode调试Electron+ts

调试Electronjs 调试Electronjs: https://www.electronjs.org/zh/docs/latest/tutorial/debugging-vscode 调试Electronts 首先看一下&#xff0c;我的目录结构。目录结构决定了launch.json中的路径部分。我将在项目根目录下进行调试&#xff0c;项目根目录下包含electron代码…

3款游戏录屏软件,记录你的精彩瞬间!

随着科技的飞速发展&#xff0c;游戏行业迎来了前所未有的繁荣。在游戏世界中&#xff0c;录屏功能成为了一项重要的辅助功能&#xff0c;它不仅能够帮助玩家记录精彩的游戏瞬间&#xff0c;还可以用于游戏教学、解说、直播等多种场景。本文将详细介绍三款主流的游戏录屏软件&a…

如何把root账号的文件修改为tank账号

linux系统 以当前用户命令创建的目录都是root的用户并且是只读的&#xff1a; 用优盘拷贝的文件及文件夹的权限是&#xff1a; 解决方案是&#xff1a;把root账号的文件修改为tank账号 在Linux系统中&#xff0c;如果您需要将属于root用户的文件或目录的所有权更改到另一个用…

裸机工程开发调试

裸机工程开发调试 嵌入式系统的组成 嵌入式系统设备遵循自底向上的基本结构 嵌入式微处理器 嵌入式操作系统 外围硬件设备 用户应用程序 ###s5p6818系统主要资源 s5p6818是三星公司推出的64位RISC微处理器&#xff0c; 其CPU采用的是ARM CortexA53内核, 基于ARMv7和ARMv8指…

U盘打不开提示格式化怎么办?(含数据恢复及U盘修复教程)

引言&#xff1a; 随着数字化时代的发展&#xff0c;U盘已成为我们日常生活和工作中不可或缺的数据存储工具。然而&#xff0c;有时我们可能会遇到U盘突然无法打开&#xff0c;并提示需要格式化的问题。这不仅会打乱我们的工作节奏&#xff0c;还可能会导致重要数据丢失。本文…

深度解析:亚马逊自养号测评系统构建全攻略

在当今日益激烈的平台竞争环境下&#xff0c;众多商家纷纷感受到传统运营模式的挑战&#xff0c;常规的广告和营销手段逐渐显得力不从心。为了突破这一困境&#xff0c;商家们开始寻求新的推广途径&#xff0c;其中&#xff0c;自养号测评作为一种新兴且有效的推广方式&#xf…

程序员之路:裁员与内卷下的生存之道

作为一名普通的程序员&#xff0c;身处这个瞬息万变的IT行业&#xff0c;面对着今年不断加剧的裁员浪潮和日益激烈的内卷竞争&#xff0c;我时常感到焦虑和不安。然而&#xff0c;正是这些挑战&#xff0c;让我们更加深入地思考了在这个行业中&#xff0c;我们该如何找到自己的…

vue3 - 150

目录 vue优势使用方式编写vue代码指令响应式数据其他 vue优势 功能全面生态好&#xff0c;语法简洁效率高&#xff0c;免去 DOM 操作苦&#xff0c;开发重任一肩挑&#xff01; 使用方式 1.通过cdn引入来将 Vue 应用到整个页面 2.或通过官方脚手架 create-vue来创建完整的v…

最短木板长度 - 贪心思维

系列文章目录 文章目录 系列文章目录前言一、题目描述二、输入描述三、输出描述四、java代码五、测试用例 前言 本人最近再练习算法&#xff0c;所以会发布自己的解题思路&#xff0c;希望大家多指教 一、题目描述 小明有 n 块木板&#xff0c;第 i ( 1 ≤ i ≤ n ) 块木板长…

如何实现短链接跳转到微信小程序?怎么保证永久有效?

家人们&#xff0c;在如今这互联网高度发达的时代&#xff0c;流量那可真是生命线啊&#xff01;那每个运营者都得面对的一个关键问题就是&#xff1a;咋有效地进行引流。今儿个&#xff0c;咱就好好唠唠咋实现短链接跳转到微信小程序&#xff0c;还有咋保证小程序短链接能永久…

如何优化文件跨区域传输的能力,提高市场核心竞争力?

随着业务的不断扩张和发展&#xff0c;越来越多的企业在异国、异地设立分支机构&#xff0c;用以覆盖更广泛的市场和客户群体&#xff0c;比如医院在不同地域设立一院、二院&#xff0c;企业在不同地域设立分部等等&#xff0c;因此存在必然的文件跨区域传输场景。由于涉及数据…

构建高效的企业培训平台:在线教育系统源码开发详解

今天&#xff0c;小编将讲解如何开发一个高效的企业培训平台&#xff0c;重点介绍在线教育系统的源码开发及其核心功能模块。 一、需求分析与系统规划 需求分析&#xff1a; 企业培训平台的目标用户包括公司管理层、培训管理员和普通员工。因此&#xff0c;系统需满足以下基…

FloodFill算法---BFS

目录 一、前言 二、算法模板套路 2.1 创建所需的全局变量&#xff1a; 2.2 BFS模板&#xff1a; 2.3 细节处理&#xff1a; 三、例题练习 3.1 例题1&#xff1a;图像渲染 3.2 例题2&#xff1a;岛屿数量 3.3 例题3&#xff1a;岛屿的最大面积 3.4 例题4&#xff1a;被…

【网络安全产品互联互通 告警信息资产信息】相关思维导图

近日&#xff0c;在某客户安全建设项目中&#xff0c;涉及安全告警事件的梳理上报。在整理及学习中发现最近&#xff08;以19年等保2.0为参考分隔“最近”&#xff09;发布的可参考标准&#xff0c;因此做了思维导图的整理。 PS&#xff1a;标准中存在引用的情况&#xff0c;过…

linux上用Jmter进行压测

在上一篇中安装好了Jmeter环境&#xff0c;在这一篇中将主要分享如何使用jmeter在linux中进行单机压测。 1.项目部署 在这里我们先简单部署一下测试环境&#xff0c;所用到的项目环境是个jar包&#xff0c;先在linux上home目录下新建app目录&#xff0c;然后通过rz命令将项目ja…

Processor System Reset IP核配置详解

本文描述了如何使用Xilinx的Vivado Design Suite环境中的工具来定制和生成 Processor System Reset IP 核。Vivado Design Suite是一个强大的FPGA设计和开发环境&#xff0c;它允许用户定制和配置各种IP核以适应他们的特定设计需求。 以下是针对如何定制IP核的步骤的简要概述&…

c#教程——索引器

前言&#xff1a; 索引器&#xff08;Indexer&#xff09;可以像操作数组一样来访问对象的元素。它允许你使用索引来访问对象中的元素&#xff0c;就像使用数组索引一样。在C#中&#xff0c;索引器的定义方式类似于属性&#xff0c;但具有类似数组的访问方式。 索引器&#x…