Linux之实现简易的shell

1.打印提示符并获取命令行

我们在使用shell的时候,发现我们在输入命令是,前面会有:有用户名版本当前路径等信息,这里我们可以用环境变量去获取:

  1 #include <stdio.h>2 #include <stdlib.h>3 4 const char* getUsername()5 {6     const char* name = getenv("USER");7     if(name) return name;8     else return "none";9 }10 11 const char* getHostname()12 {13     const char* hostname = getenv("HOSTNAME");14     if(hostname) return hostname;15     else return "none";16 }17 18 const char* getCwd()19 {20     const char* cwd = getenv("PWD");21     if(cwd) return cwd;22     else return "none";23 }24 25 int main()26 {27    printf("%s@%s %s\n",getUsername(),getHostname(),getCwd());                                                                                                                                                 28     return 0;29 }

写。

看到我们打印出来的是绝对路径, 而shell显示的相对路径, 但为了区分先这样不去裁剪. 

 1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4                                                                                                            5 #define NUM 1024                                                                                           6                                                                                                            7 const char* getUsername()                                                                                  8 {                                                                                                          9     const char* name = getenv("USER");                                                                     10     if(name) return name;                                                                                  11     else return "none";                                                                                    12 }                                                                                                          13                                                                                                            14 const char* getHostname()                                                                                  15 {                                                                                                          16     const char* hostname = getenv("HOSTNAME");                                                             17     if(hostname) return hostname;                                                                          18     else return "none";                                                                                    19 }                                                                                                          20                                                                                                            21 const char* getCwd()                                                                                       22 {                                                                                                          23     const char* cwd = getenv("PWD");                                                                       24     if(cwd) return cwd;                                                                                    25     else return "none";                                                                                    26 }                                                                                                          27                                                                                                            28 int getUsercommand(char* command, int num)                                                                 29 {                                                                                                          30     printf("[%s@%s %s]",getUsername(),getHostname(),getCwd()); 31     char* r = fgets(command,num,stdin);//最终还是会输入\n32     if(r == NULL) return 1;33                          34     command[strlen(command)-1] = '\0';//去除输入的换行35     return 0;            36 }                        37                          38 int main()               39 {                        40     char usercommand[NUM];41     //1.打印提示符并且获取命令字符串42     getUsercommand(usercommand,sizeof(usercommand));43     //2.                 44     //3.                                                                                                                                                                                                      45     printf("%s",usercommand);//回显命令,用于测试46     return 0;47 }

 由于用scanf接收的遇到空格就会停止读取, 所以用fgets, 而且用户输入完命令一定会输入回车, 所以把最后一个回车符删掉.


2.解析命令行

我们在输入命令时, 可能不仅仅只是一段,比如说:"ls -a -l "。但是命令行解释器内部在解析指令时应该传递的是"ls" "-a" "-l"这样的多个个字符串, 所以我们还需要以空格来分割字符串。

    1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #define DEBUG 16 #define NUM 10247 #define SIZE 648 #define SEP " "41 void commandSplit(char* in, char* out[])42 {43     int argc = 1;44     out[0] = strtok(in,SEP);
W> 45     while(out[argc++] = strtok(NULL,SEP));//报警不需要处理46 47 #ifdef DEBUG 48     for(int i = 0; out[i]; i++)49         printf("%d:%s\n",i,out[i]);50 #endif51 }52 53 int main()54 {55     char usercommand[NUM];56     char* argv[SIZE];57     //1.打印提示符并且获取命令字符串58     getUsercommand(usercommand,sizeof(usercommand));59     //2.分割字符串60     commandSplit(usercommand, argv); 61     //3.                                                                                                                                                                                                    62     return 0;63 }            


3.执行对应的命令 

创建子进程和进程替换, 为了不影响shell, 我们将大部分指令的执行让子进程去完成, 父进程只要阻塞等待子进程完成就好了。

    1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 #include <unistd.h>5 #include <sys/types.h>6 #include <sys/wait.h>7 8 //#define DEBUG 19 #define NUM 102410 #define SIZE 6411 #define SEP " "12 13 const char* getUsername()14 {15     const char* name = getenv("USER");16     if(name) return name;17     else return "none";18 }19 20 const char* getHostname()21 {22     const char* hostname = getenv("HOSTNAME");23     if(hostname) return hostname;24     else return "none";25 }26 27 const char* getCwd()28 {29     const char* cwd = getenv("PWD");30     if(cwd) return cwd;31     else return "none";32 }33 34 int getUsercommand(char* command, int num)                                                                                             35 {36     printf("[%s@%s %s]",getUsername(),getHostname(),getCwd()); 37     char* r = fgets(command,num,stdin);//最终还是会输入\n38     if(r == NULL) return -1;39                                                                                                                                       40     command[strlen(command)-1] = '\0';//去除输入的换行41     return strlen(command);42 }43 44 void commandSplit(char* in, char* out[])45 {46     int argc = 1;47     out[0] = strtok(in,SEP);
W> 48     while(out[argc++] = strtok(NULL,SEP));//报警不需要处理49 50 #ifdef DEBUG 51     for(int i = 0; out[i]; i++)52         printf("%d:%s\n",i,out[i]);53 #endif54 }55 56 int execute(char* argv[])57 {58     pid_t id = fork();59     if(id < 0) return 1;60     else if(id == 0)61     {62         //child63         //exec commond64         execvp(argv[0],argv);65         exit(1);66     }67 68     else69     {70         //father71         pid_t rid = waitpid(id,NULL,0);72         if(rid < 0)73             printf("wait fail\n");74     }75 76     return 0;77 }78 79 int main()80 {81     while(1)82     {83         char usercommand[NUM];84         char* argv[SIZE];85         //1.打印提示符并且获取命令字符串86         int n = getUsercommand(usercommand,sizeof(usercommand));87         if(n <= 0) continue;//如果得到的是空串或者获取失败,不要往后执行88         //2.分割字符串89         commandSplit(usercommand, argv);90         //3.执行命令91         execute(argv);                                                                                                                 92     }93     return 0;94 }

 由于shell要一直运行, 所以要循环执行, 这里程序替换用execvp函数比较合适, 因为argv数组就是我们分割出的一个个命令的子串, argv[0]就是程序名, argv就是指令集. 父进程只进行wait即可. 

此外, getUsercommand函数可以优化一下, 返回的是输入的指令的长度, 如果接收失败(返回值为-1或者返回值是0只打印了空行)就不需要往下执行了, 直接continue进行下一轮.


4.特殊处理

 有一批命令, 不能让子进程执行, 必须让父进程自己执行, 这些命令叫内建命令.

1) cd指令

可以看到cd .. 之后并没有发生什么异常, 但是pwd之后发现路径没有发生变化. 

我们为什么能在linux中进入某个目录, 就是因为我们改变了shell的工作目录. 每个进程都有自己的工作目录, 我们想让父进程的工作目录发生改变, 但是程序替换之后都是子进程在执行cd .., 改变的都是子进程的工作目录, 子进程改变完了又被回收了, 父进程完全没发生变化, 所以cd应该实现成内建命令。

   79 void cd(const char* path)80 {81     chdir(path);82 }83 84 //1->yes,0->no85 int doBuildin(char* argv[])86 {87     if(strcmp(argv[0],"cd") == 0)88     {89         char* path = NULL;
W> 90         if(argv[1] == NULL) path = ".";91         else path = argv[1];92         cd(path);93         return 1;94     }95     else if(strcmp(argv[0],"ls")==0)96     {97         return 1;98     }99     return 0;                                                                                                                         100 }101 102 int main()103 {104     while(1)105     {106         char usercommand[NUM];107         char* argv[SIZE];108         //1.打印提示符并且获取命令字符串109         int n = getUsercommand(usercommand,sizeof(usercommand));110         if(n <= 0) continue;//如果得到的是空串或者获取失败,不要往后执行111         //2.分割字符串112         commandSplit(usercommand, argv);113         //3.检查是不是内建命令,是的话直接执行114         n = doBuildin(argv);115         if(n) continue;//是内建命令不用往后执行了116         //4.执行命令117         execute(argv);118     }119     return 0;120 }

所以在执行命令前先检查是不是内建命令, 用返回值接收, 如果是就直接执行并返回1, continue不往下执行, 如果不是就返回0, 执行命令. 

 既然当前的工作目录改变了, 那么环境变量PWD也要改变: 

chdir改变当前工作目录, getcwd获取当前的工作路径, sprintf将tmp中的内容输出到cwd中, putenv将cwd导入环境变量. 


2) export命令 

 

创建一个数组env储存要导入的环境变量, 设置size指向导入到第几个环境变量.

如果argv[1]是空就直接返回, 否则就导入环境变量, 注意不能直接把argv[1]导入进去, 因为argv[1]随着指令的输入时刻在变化, 需要开辟额外的空间去存储.


3)echo指令


4)ls指令

我们执行的ls指令中不同的文件都有不同的颜色,所以对于ls我们可以在分割命令的时候加上一个“--color=auto”.


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

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

相关文章

python如何快速查找到想要的文档

字多不看版&#xff0c;直接体验 待补充 演示代码 # -*- coding:UTF-8 -*-# region 导入必要的依赖包 import os import subprocess from enum import Enum模块名 pyperclip try:import pyperclip # 需要安装 pyperclip 模块&#xff0c;以支持粘贴板操作 except ImportEr…

PTA-成绩转换

本题要求编写程序将一个百分制成绩转换为五分制成绩。转换规则&#xff1a; 大于等于90分为A&#xff1b;小于90且大于等于80为B&#xff1b;小于80且大于等于70为C&#xff1b;小于70且大于等于60为D&#xff1b;小于60为E。 输入格式: 输入在一行中给出一个整数的百分制成…

羊大师教你如何科学控制体重,轻松瘦下来

羊大师教你如何科学控制体重&#xff0c;轻松瘦下来 我们都知道&#xff0c;控制体重对于保持健康和美丽至关重要。然而&#xff0c;许多人在减肥的道路上走得波折重重&#xff0c;常常陷入挫败和不知所措的境地。那么&#xff0c;如何科学控制体重&#xff0c;轻松瘦下来呢&a…

项目经理只需要有PMP证书就行?

就目前而言&#xff0c;大部分人对于项目经理的认识还停留在&#xff1a;有项目管理经验&#xff0c;有对应的工作年限&#xff0c;有PMP证书。所以绝大多数人都认为只要报考了PMP项目管理&#xff0c;取得PMP证书&#xff0c;即可加入项目经理的圈子&#xff0c;薪资翻倍。 但…

协同过滤与矩阵分解讲解(PPT)

总览 协同过滤算法&#xff0c;就是一种完全依赖用户和物品之间行为关系的推荐算法。 从字面理解&#xff0c;协同大家的反馈、评价和意见一起对海量的信息进行过滤&#xff0c;从中筛选出用户可能感兴趣的信息。 知识概括 从这几个方面进行分析。 一、基于用户的协同过滤 显示…

6个PPT素材网站,让你快速做出好看的PPT

找PPT模板一定要收藏好这6个网站&#xff0c;能让你快速做出好看的PPT&#xff0c;重点十可以免费下载&#xff0c;赶紧收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/search/ppt/0_0_0_1.html?vNTYwNDUx 菜鸟图库网有非常丰富的免费素材&#xff0c;像设计类、办公…

力扣labuladong——一刷day48

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣1602. 找到二叉树中最近的右侧节点二、力扣437. 路径总和 III三、力扣560. 和为 K 的子数组 前言 二叉树的递归分为「遍历」和「分解问题」两种思维模式…

第7章-使用统计方法进行变量有效性测试-7.4.2-多元线性回归

目录 多元线性回归模型 总体回归函数 样本回归函数 线性回归模型的假定 普通最小二乘法&#xff08;Ordinary Least Squares&#xff0c;OLS&#xff09; 拟合优度指标 F检验 回归系数的t检验 Python中构建多元线性回归模型 数据理解 数据读取 数据清洗 相关分析 …

想考教师编制专业不对口怎么办?

很多人在想要步入教师行业时&#xff0c;会遇到一个问题&#xff1a;专业不对口。这种情况可能会让你感到困惑和沮丧&#xff0c;但不要气馁&#xff0c;因为有很多方法可以让你实现自己的梦想。 可以通过提高自己的教育水平和能力来弥补专业不对口的缺陷。你可以通过参加教师资…

品牌小红书koc投放策略分享,纯干货!

作为中国具有影响力的时尚美妆社交平台&#xff0c;小红书与其充满活力的用户群体成为品牌寻找优质KOC合作的理想平台。本文伯乐网络传媒将探讨品牌如何利用小红书的KOC投放策略&#xff0c;实现更广泛的市场覆盖和更有效的品牌营销。 一、明确目标受众与KOC合作需求 在开始策…

containerd Snapshots功能解析

containerd Snapshots功能解析 snapshot是containerd的一个核心功能&#xff0c;用于创建和管理容器的文件系统。 本篇containerd版本为v1.7.9。 本文以 ctr i pull命令为例&#xff0c;分析containerd的snapshot “创建” 相关的功能。 ctr命令 ctr image相关命令的实现在cmd…

《人件》读书笔记

文章目录 一、书名和作者二、书籍概览2.1 主要论点和结构2.2 目标读者和应用场景 三、核心观点与主题3.1 管理团队主题3.2 改善工作环境主题3.3 正确的人主题3.4 团队项目管理主题 四、亮点与启发4.1 最有影响的观点4.2 对个人专业发展的启示 五、批评与局限性5.1 可能存在争议…

leetcode (力扣) 97. 交错字符串(动态规划)

文章目录 题目描述思路分析完整代码 题目描述 给定三个字符串 s1、s2、s3&#xff0c;请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。 两个字符串 s 和 t 交错 的定义与过程如下&#xff0c;其中每个字符串都会被分割成若干 非空 子字符串&#xff1a; s s1 s2 … sn t …

数据库——查询连续的月份

一、GP或PGSQL with recursive t(n) as (select date(2023-01-01) union all select n1 from t where n < now()) select to_char(n, yyyy-mm) as ny from t group by ny order by ny 二、Hive select add_months(FROM_UNIXTIME(unix_timestamp(SUBSTR(start_date, 1, 7…

rdf-file:组件内置协议(SP、DE、FUND、FUND_INDEX)

Rdf-File根据协议布局模板和数据定义模板,来进行文件的解析与生成。通过协议布局和数据定义模板&#xff0c;能够明确计算出头尾占用的行数&#xff0c;这样可以更精确的分离出head&#xff0c;body&#xff0c;tail。 目前组件实现的协议布局模板可以分为如下两大类&#xff…

【深度学习实验】图像处理(一):Python Imaging Library(PIL)库:图像读取、写入、复制、粘贴、几何变换、图像增强、图像滤波

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 安装 PIL 库1. 图像读取和写入a. 图像读取b. 图像写入c. 构建新图像 2. 图像复制粘贴a. 图像复制b. 图像局部复制c. 图像粘贴 3. 几何变换a. 图像调整大小b. 图像旋转c. 图像翻转 4. 图像增强a.…

cocos creator中AStar算法实例

引擎版本 —— cocos creator2.3.4

高端猫罐头有哪些?精选的5款优质的猫罐头推荐!

很多铲屎官看猫猫吃猫粮吃腻了&#xff0c;或者猫猫平时不喜欢喝水&#xff0c;又或者看猫猫太瘦了就想入手几款猫罐头但是又愁于不会选择&#xff0c;而且现在猫罐头风这么大不知道选什么好~ 作为一个开宠物店7年的店长&#xff0c;对于猫咪的饮食健康我一直都很重视&#xff…

数据结构与算法编程题16

已知长度为n的线性表A&#xff0c;请写一时间复杂度为O(n)、空间复杂度为O(1)的算法&#xff0c;该算法删除线性表中所有值为item的数据元素。 item 3 数组下标 i 0 1 2 3 4 5 6 7 8 顺序表&#xff1a; 1 2 3 4 3 3 5 3 7 #include <iostream> using namespace std;typ…

Linux常用基础命令及重要目录,配置文件功能介绍

目录 一&#xff0c;Linux常用必备基础命令 1&#xff0c;网络类命令 2&#xff0c;文件目录类命令 3&#xff0c;操作类命令 4&#xff0c;关机重启命令 5&#xff0c;帮助命令 6&#xff0c;查看显示类命令 7&#xff0c;命令常用快捷键 二&#xff0c;Linux重要目录…