Linux基础 7:自定义shell的编写

news2024/9/23 7:23:10

自定义shell的编写

  • 一.自定义shell的编写。
    • 1.打印命令行提示符。
    • 2.获取用户输入字符串进行字符串分割保存。
    • 3.调用系统调用接口执行命令(使用子进程)
    • 4.对于内建命令的特殊处理。
      • 1.cd
      • 2.cd 特殊符号识别:
    • 5.export和echo作为内建命令:
      • 1.export
      • 2.echo
  • 二 . 文件部分:
    • 1.语言级别的IO输入:
      • 1.程序默认打开的三个流:
    • 2.操作系统上的接口:
      • 1.基本概念:
      • 2.系统调用接口:
      • 3.文件描述符:
      • 4.语言方面的文件结构体:
      • 5.系统方面的文件结构体:
      • 6.read write close 访问过程!
    • 3.实现重定向:
      • 1.fd的分配规则:
      • 2.重定向的优化:
    • 4.缓冲区的意义:
      • 1.无缓冲&&行缓冲&&完全缓冲
      • 2.证明语言层缓冲区的存在:
      • 3.缓冲区的语言结构---->os底层结构:
    • 5.实现一个FILE类型的结构体封装系统调用:
      • 1.mystdio.h
      • 2.mystdio.c
      • 3.file_text.c

一.自定义shell的编写。

1.打印命令行提示符。

请添加图片描述

1.命令行提示符由哪几部分组成?
[用户名@主机名 文件路径]$
2.通过getenv函数获取环境变量内容返回对应字符串。
3.变量保存字符串然后打印。

void Get_Hint()
{
  char* Username = getenv("USER");
  char* Hostname = getenv("HOSTNAME");
  char* Pwd = getenv("PWD");

  if(strcmp(Username,"root")==0)
  {
   printf("[root@%s %s]#",Hostname,Pwd);
  }
  else
  {
   printf("[%s@%s %s]$",Username,Hostname,Pwd);
  }
}

2.获取用户输入字符串进行字符串分割保存。

1.变量保存获取到的一行字符串。
2.定义全局的char* 类型的数组保存数据。

void Get_String(char* arr)
{
  //1.获取一行字符串
  scanf("%[^\n]s",arr);
}


void Slicing(char* arr)
{
  int i=0;
  slistr[i++] = strtok(arr,Group);
  while(slistr[i++] = strtok(NULL,Group));
  //1.命令显示颜色的条件:
  if(strcmp(slistr[0],"ls")==0)
  {
    slistr[i-1] = (char*)("--color");
    slistr[i] = NULL;
  }
} 

3.调用系统调用接口执行命令(使用子进程)

在这里插入图片描述

1,使用子进程去执行接口命令。
2.int execvp(const char* file , char * const argv[]);
3.参数一传命令 ,参数二传命令+选项的一个char* 类型的数组。
4.可以去执行非内建命令。

void execute()
{
    int id = fork();
    if (id == 0)
     {
        execvp(slistr[0], slistr);
        exit(1);
     }
     int status = 0;
     waitpid(id, &status, 0);
}

在这里插入图片描述

4.对于内建命令的特殊处理。

在这里插入图片描述

1.cd

1,内建命令:执行命令应该要改变当前的路径,使用子进程去执行的命令影响不了bash进程。改变不了操作系统当前的一个工作路径。应该让父进程去执行cd命令才可以改变bash工作路径。
2.进入execute函数进行判断当前执行的命令是不是内建命令或者普通命令。
3.内建命令需要进行单独的判断:

在这里插入图片描述

请添加图片描述

2.cd 特殊符号识别:

1.我们知道cd存在特殊符号可以通过getcwd()函数去获取当前的工作路径。
2.通过putenv()函数进行环境变量的更新。
3.有效的解决特殊符号存在环境变量中的问题!

void cmd()
{

    //1.考虑对内建命令进行处理:
    if(strcmp(slistr[0],"cd")==0)
    {
      if(strcmp(slistr[1],"~")==0)
      {
        strcpy(nearpath,getenv("PWD"));
        chdir(getenv("OLDPWD"));
      }
      else if (strcmp(slistr[1],"-")==0)
      {
        if(flagnear == 0)
        {
          strcpy(nearpath,getenv("PWD"));
          chdir(nearpath);
          flagnear  = 1;
          return;
        }
        else
        {
          char* tmp = getenv("PWD");
          chdir(nearpath);
          strcpy(nearpath,tmp);
        }
      }
      else if (strcmp(slistr[1],"..")==0 || strcmp(slistr[1],"../")==0)
      {
        strcpy(nearpath,getenv("PWD"));
        chdir("..");

      }
      else
      {
        strcpy(nearpath,getenv("PWD"));
        chdir(slistr[1]);
      }
      
      //更新环境变量
      char temp[1024];
      //获取当前的工作路径!
      getcwd(temp,1024);
      snprintf(path,SIZE,"PWD=%s",temp);
      putenv(path);
      return;
    }
}

请添加图片描述
在这里插入图片描述

5.export和echo作为内建命令:

1.export

1.export设置一个新的环境变量:
2.使用setenv()
3.当overwrite值为0时,按照name和value去给环境变量新增内容:
在这里插入图片描述

void Export(int * flag)
{
  if(strcmp(slistr[0],"export")==0)
  {
    char str[2048] = {0};
    strcpy(str,slistr[1]);
    int pos = 0;
    while(str[pos++] != '=');
    str[pos-1] = '\0';

    setenv(str,str+pos,0);
    *flag = 1;
  }
  return;
}

2.echo

在这里插入图片描述

1.echo 空格 ---->打印空格:
2.echo 字符串 -----> 打印字符串:
3.echo $环境变量名称 ---->打印环境变量内容
4.echo $?打印最近一次的进程退出码。


void execute()
{
    int flag = 0;
    //1.cd 判断
    if(flag == 0)
      cmd(&flag); 
    //2.export判断
    if(flag == 0)
      Export(&flag);
    //3.echo判断:
    if(flag == 0)
      Echo(&flag);
    //4.创建子进程处理普通命令
    if(flag == 0)
    {

      int id = fork();
      if(id == 0)
      {
        execvp(slistr[0],slistr);
        exit(1);
      }
      int status = 0;
      pid_t rid = waitpid(id, &status, 0);
      if(rid == id) lastcode = WEXITSTATUS(status); 
    }
}


void Echo(int* flag)
{
  if(strcmp(slistr[0],"echo")==0)
  {
    if(slistr[1] == NULL)
      printf("\n");
    else if (slistr[1][0] == '$')
    {
      if(slistr[1][1] == '?')
        printf("%d\n",lastcode);
      else
        printf("%s\n",getenv(slistr[1]+1));
    }
    else 
      printf("%s\n",slistr[1]);

    *flag = 1;
  }
}

在这里插入图片描述

二 . 文件部分:

1.语言级别的IO输入:

1.程序默认打开的三个流:

1.标准输入流:stdin ---->键盘
2.标准输出流:stdout ---->显示器
3.标准错误流:stderr ---->显示器

在这里插入图片描述

1.因为程序开始执行就默认打开三个流。
2.stdin stdout stdree 类型FILE* 类型的变量。
3.我们使用语言的文件操作都会使用fopen函数打开一个文件流!
4.使用fclose去关闭一个文件流。

功能------------ 函数名------------ 适用于
字符输入函数-- fgetc ---------------所有输入流
字符输出函数 --fputc ---------------所有输出流
文本行输入函数-- fgets------------ 所有输入流
文本行输出函数 --fputs ------------所有输出流
格式化输入函数 --fscanf ----------所有输入流
格式化输出函数 --fprintf -----------所有输出流
二进制输入 --fread ------------------文件
二进制输出 —fwrite ------------------文件

2.操作系统上的接口:

在这里插入图片描述

1.基本概念:

1.操作系统提供系统调用接口,语言方面去进行上层的封装。
2.我们C语言的文件操作就是封装了操作系统提供的系统调用接口。

2.系统调用接口:

fopen----->open

在这里插入图片描述

1.int open(const char *pathname, int flags, mode_t mode);
2.pathname:需要打开的文件名称。
3.flags:是一个位图32位的有效保存不同的文件打开要求。
4.mode_t mode:是一个八进制的文件权限掩码(写文件并且文件不存在)

在这里插入图片描述

1.O_RDONLY : 读权限
2.O_WRONLY : 写权限
3.O_RDWR : 可读可写权限 ----->一个open只能使用上面三个的一个。
4.O_CREAT : 读一个文件但是这个文件没有创建。
5.补充:O_APPEND : 写文件的时候可以进行文件内容的追加。
6.O_TRUNC : 写一个文件从头开始之前的内容删除。
综上所述:可以使用|把上面的内容搭配在一起可以实现C语言文件操作上的只读,只写(没有创建) , 追加 , 内容。

   18 int main()
   19 {
   20   //1.写方式打开文件如果没有就创建再一次打开覆盖写入:
   21   int id = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);
   22   //2.写方式打开文件如果没有就创建再一次打开追加内容: 
E> 23   int id = open("log.txt",O_WRONLY|O_CREAT|O_APPEND);                                                                              
   24   return 0;
   25 }

3.文件描述符:

1.概念:open函数的返回值就是文件描述符:
2.在task_struct 中保存了一个指针指向一个数组,数组中保存了一个file*
3.数组的下标就是文件描述符:
4.进程默认打开三个文件,开始的时候0 , 1 , 2 下标就被使用!
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.语言方面的文件结构体:

1.FILE*
2.stdin stdout stderr 都是FILE* 类型的数据。
3.FILE中一定有一个成员是int fd(文件标识符)
4.fopen函数访问FILE对象,调用成员内容本质还是底层封装了系统调用接口。

5.系统方面的文件结构体:

1.file 类型是系统封装的文件类型。
2.主要包括三个内容:属性 缓冲区 方法集
3.方法集:函数指针保存对应外设提供的方法。
4.缓冲区:保存数据
5.属性:文件属性

6.read write close 访问过程!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

访问过程:第一个参数就是文件描述符表,根据struct files_struct * files 和fd找到对应文件的指针,访问操作系统的文件结构体,然后对文件结构体做操作。可以实现read write close 。

3.实现重定向:

1.fd的分配规则:

1.stdin stdout stderr 分配为0 , 1 , 2 三个下标。
2.关闭一个stdin —> open一个新的文件那么0位置中保存新的文件指针。
3.通过关闭+创建的方式可以实现文件的重定向。

2.重定向的优化:

在这里插入图片描述

1.使用dup2命令可以帮我们把数组中的下标位置的文件指针做修改。
2.具体规则如下:
1.如何完成重定向。
2.oldfd —>新的文件的fd
3.newfd是三个流的任意一个。
4.dup2(fd , 0)dup2(fd , 1)dup2(fd , 2)
5.第2个参数的不同实现输入重定向输出重定向错误重定向

在这里插入图片描述

//定义的宏
#define IgnSpace(buf,pos) do{ while(isspace(buf[pos])) pos++; }while(0)

void CheckRedir(char in[])
{
    // ls -a -l
    // ls -a -l > log.txt
    // ls -a -l >> log.txt
    // cat < log.txt
    redir_type = NoneRedir;
    filename = NULL;
    int pos = strlen(in) - 1;
    while( pos >= 0 )
    {
        if(in[pos] == '>')
        {
            if(in[pos-1] == '>')
            {
                redir_type = AppendRedir;
                in[pos-1] = STREND;
                pos++;
                IgnSpace(in, pos);
                filename = in+pos;
                break;
            }
            else
            {
                redir_type = StdoutRedir;
                in[pos++] = STREND;
                IgnSpace(in, pos);
                filename = in+pos;
                //printf("debug: %s, %d\n", filename, redir_type);
                break;
            }
        }
        else if(in[pos] == '<')
        {
            redir_type = StdinRedir;
            in[pos++] = STREND;
            IgnSpace(in, pos);
            filename = in+pos;
            //printf("debug: %s, %d\n", filename, redir_type);
            break;
        }
        else
        {
            pos--;
        }
    }
}

4.缓冲区的意义:

1.开辟内存+提高效率。
2.缓冲区和操作系统内核没有关系,语言层自带缓冲区:

1.无缓冲&&行缓冲&&完全缓冲

1.程序结束语言自动刷新数据到系统。
2.程序默认是行刷新!
3.特殊情况改变语言缓冲区到os缓冲区的刷新方式。

2.证明语言层缓冲区的存在:

1.向显示器打印,刷新方案就是行刷新。
2.向文件中进行重定向,刷新方案变成全缓冲。
3.产生子进程发生写时拷贝。
4.子进程拷贝一份父进程缓冲区数据。
5.结尾进行全部刷新产生两份12345666666

在这里插入图片描述

在这里插入图片描述

3.缓冲区的语言结构---->os底层结构:

在这里插入图片描述

5.实现一个FILE类型的结构体封装系统调用:

1.mystdio.h

#pragma once

#include <stdio.h>

#define SIZE 4096

typedef struct _myFILE
{
    //char inbuffer[];
    char outbuffer[SIZE];
    int pos;
    int cap;
    int fileno;
}myFILE;


myFILE *my_fopen(const char *pathname, const char *mode);
int my_fwrite(myFILE *fp, const char *s, int size);
void my_fclose(myFILE *fp);

2.mystdio.c

#include "mystdio.h"
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>


myFILE *my_fopen(const char *pathname, const char *mode)
{
    int flag = 0;
    if(strcmp(mode, "r") == 0)
    {
        flag |= O_RDONLY;
    }
    else if(strcmp(mode, "w") == 0)
    {
        flag |= (O_CREAT|O_WRONLY|O_TRUNC);
    }
    else if(strcmp(mode, "a") == 0)
    {
        flag |= (O_CREAT|O_WRONLY|O_APPEND);
    }
    else
    {
        return NULL;
    }

    int fd = 0;
    if(flag & O_WRONLY)
    {
        umask(0);
        fd = open(pathname, flag, 0666);
    }
    else
    {
        fd = open(pathname, flag);
    }
    if(fd < 0) return NULL;

    myFILE *fp = (myFILE*)malloc(sizeof(myFILE));
    if(fp == NULL) return NULL;
    fp->fileno = fd;

    return fp;
}
int my_fwrite(myFILE *fp, const char *s, int size)
{
    return write(fp->fileno, s, size);
}
void my_fclose(myFILE *fp)
{
    close(fp->fileno);
    free(fp);
}

3.file_text.c

#include "mystdio.h"
#include <string.h>

const char *filename = "./log.txt";

int main()
{
    myFILE *fp = my_fopen(filename, "w");
    if(fp == NULL) return 1;

    const char *s = "hello myself stdio interface\n";
    my_fwrite(fp, s, strlen(s));

    my_fclose(fp);
    return 0;
}

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

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

相关文章

动态规划3,地下城游戏

思路&#xff1a; 经验题目要求 a.以某个位置为结尾… dp[i][j]表示&#xff1a;走到【i&#xff0c;j】位置的时候&#xff0c;所需要的最初最低血量。 b.以某个位置为起点 dp[i][j]表示&#xff0c;从【i&#xff0c;j】出发&#xff0c;到达终点&#xff0c;所需要的最低初…

微信小程序云开发入门

写在前面&#xff1a; 参考的课程是咸虾米老师在b站的课&#xff1a;2.2.在页面展现云数据库的内容_哔哩哔哩_bilibili 云开发官方文档&#xff1a;微信开放文档 1、什么是云开发&#xff1f; 用自己的话来说就是把服务器和后台都搭在腾讯开发的服务器上。 2、如何开通云开发…

HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-WatchDog

目录 一、 WATCHDOG 概述功能简介基本概念 二、WATCHDOG 模块相关API三、WATCHDOG HDF驱动开发3.1、开发步骤(待续...) 坚持就有收获 一、 WATCHDOG 概述 功能简介 看门狗&#xff08;Watchdog&#xff09;&#xff0c;又称看门狗计时器&#xff08;Watchdog timer&#xff0…

【AI数字人-论文】RAD-NeRF论文

文章目录 前言模型框架动态的NeRF前处理头部模型音频特征眼部控制头部总体表示 躯干模型loss 结果参考 【AI数字人-论文】AD-NeRF论文 前言 本篇论文有三个主要贡献点&#xff1a; 提出一种分解的音频空间编码模块&#xff0c;该模块使用两个低维特征网格有效地建模固有高维音…

关于Kinect 互动沙盘 深度图 Shader Graph 分层

把Kinect的深度图穿给Shader Graph using com.rfilkov.kinect; using UnityEngine; using UnityEngine.UI; public class GetDepthTex : MonoBehaviour { public Material Mat_SandTable; void Update() { Mat_SandTable.SetTexture("_MainTex"…

LabVIEW串口通信的激光器模块智能控制

LabVIEW串口通信的激光器模块智能控制 介绍了通过于LabVIEW的VISA串口通信技术在激光器模块控制中的应用。通过研究VISA串口通信的方法和流程&#xff0c;实现了对激光器模块的有效控制&#xff0c;解决了数据发送格式的匹配问题&#xff0c;为激光器模块的智能控制提供了一种…

Open CASCADE学习|几何数据结构

在几何引擎内一般把数据分成两类&#xff1a;几何信息与拓扑信息。二者可以完整地表达出实体模型&#xff0c;彼此相互独立、又互相关联。几何信息是指构成几何实体的各几何元素在欧式空间中的位置、大小、尺寸和形状信息。例如一条空间的直线&#xff0c;可以用两端点的位置矢…

019—pandas 计算实验仪器正常运行周期时长

需求&#xff1a; 对指定两个状态作为一个周期&#xff0c;并计算出周期内的差值&#xff0c;写到周期结束所在的行上。pandas 非常适合实现此类有着较为复杂逻辑的问题。 思路&#xff1a; 这个问题的难点是状态的不规律性&#xff0c;如何才能准确找出所有 T 和 C 的周期。…

python 打包 apk

转换之前python代码需要使用指定的框架才能转换&#xff0c;列如&#xff1a;kivy from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Buttonimport time import pyautogui import threadingstatus False# 这是一个将被线程执…

本博客工程源码总目录----方便你快速找到自己喜欢的项目

目录 1、前言2、本人项目总分类3、FPGA图像处理类项目-->快速查找3.1、图像采集-->MIPI视频类3.2、图像采集-->SDI视频类3.3、图像采集-->PAL视频类3.4、图像采集-->Cmeralink视频类3.5、图像转换-->LVDS视频转换3.6、图像缩放&#xff08;纯Verilog版本HLS版…

QEMU开发入门

1. 简介 QEMU&#xff08;Quick EMUlator&#xff09;是一个开源的虚拟化软件&#xff0c;它能够模拟多种硬件平台&#xff0c;并在这些平台上运行各种操作系统。QEMU可以在不同的主机架构之间进行虚拟化&#xff0c;例如x86、ARM、PowerPC、Risc-V等。QEMU是一个功能强大且灵…

【竞技宝jjb.lol】LOL:wayward奎桑提主宰团战 WE2-1力克IG

北京时间2024年2月24日&#xff0c;英雄联盟LPL2024春季常规赛继续进行&#xff0c;昨日共进行三场比赛&#xff0c;第二场比赛由IG对阵WE。本场比赛双方前两局战至1-1平&#xff0c;决胜局WE中期抓住IG失误后拿下大龙奠定胜局&#xff0c;最终WE2-1力克IG。以下是本场比赛的详…

二次元风格个人主页HTML源码

源码介绍 直接上传服务器压缩包解压就完事了&#xff0c;修改index.html内代码即可&#xff0c;注释写的很全&#xff0c;替换图片在文件夹img&#xff0c;只有前端&#xff0c;没有后台&#xff0c;大佬如果需要&#xff0c;可以自行添加后台。本源码非常适合个人工作室主页。…

利用psutil库检查脚本是否在运行

摘要 如果要判断某一脚本是否在运行&#xff0c;可以通过psutil库获取所有进程的cmdline&#xff0c;并判断指定的文件名是否在cmdline中。 目录 1.psutil库简介 2.检查代码及说明 2.1检查思路 2.2异常捕获 2.3执行方法 1.psutil库简介 psutil 是一个跨平台&#xff08;…

Optimization for Deep Learning

Notations: : model parameters at time step or : gradient at used to compute : momentum accumulated from time step to time step , which is used to cpmpute Optimization What is Optimization about? 找到一组参数&#xff0c;使得 最小&#xff0c;或者说是…

内容安全补充

第十一天 密码学 近现代加密算法 古典加密技术 --- 算法保密原则 近现代加密技术 --- 算法公开&#xff0c;密钥保密 对称加密算法&#xff0c;非对称加密算法 对称加密 --- 加密和解密的过程中使用的是同一把密钥。 所以&#xff0c;对称加密所使用的算法一定是一种双向…

概率基础——指数分布

概率基础——指数分布 介绍 指数分布是一种连续概率分布&#xff0c;描述了独立随机事件之间的时间间隔。它常被用来模拟随机事件的等待时间&#xff0c;例如到达下一位顾客的等待时间、设备故障的间隔时间等。指数分布具有无记忆性的特点&#xff0c;即在给定时间内没有发生…

*MYSQL--索引--内部原理

MYSQL的索引根据功能,主要有三大类型: 1.HASH索引 2.二叉树 3.BTREE索引 一:HASH索引 1.内部原理: 在设置了某列为索引列之后,并且开始或者将要在相应索引列创建数据的时候,系统通过某种算法 F(X) 自动计算出来一个十六进制的哈希值,这个哈希值能够对应相应的字段值 所以…

单片机51 输入和输出

一、IO口基本概念介绍 单片机的IO口&#xff08;Input/Output口&#xff09;是连接单片机与外部电路或设备的接口。单片机的IO口可以分为输入口和输出口两种&#xff0c;用于控制和监测外部设备的状态。 1. 输入口&#xff1a;单片机的输入口用于接收外部电路或设备的信号。输…

C++的string容器->基本概念、构造函数、赋值操作、字符串拼接、查找和替换、字符串比较、字符存取、插入和删除、子串

#include<iostream> using namespace std; #include <string> //string的构造函数 /* -string(); //创建一个空的字符串 例如: string str; -string(const char* s); //使用字符串s初始化 -string(const string& str); //使…