从零开始学习Linux(8)----自定义shell
shell从用户读入字符串“ls”,shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序,并等待这个进程结束。所以要写一个shell,需要循环以下过程:
#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, 否 0if(strcmp("cd", argv[0]) == 0){// 2. 执行// 标记为内建命令 ret = 1;// 获取 cd 命令的参数(要切换到的目录),如果没有参数则默认为家目录 char *target = argv[1]; //cd XXX or cdif(!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;
}
相关文章:

从零开始学习Linux(8)----自定义shell
shell从用户读入字符串“ls”,shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序,并等待这个进程结束。所以要写一个shell&…...

《大数据分析-数据仓库项目实战》学习笔记
目录 基本概念 数据仓库 数据仓库整体技术架构 数据仓库主题 数据集市 数据仓库的血缘关系 数据仓库元数据管理 数据仓库的指标 数据仓库维度概念 HDFS Flume Hadoop Kafka 数据仓库分层模型 Superset 即席查询 Sqoop Atlas元数据管理 项目需求描述 系统目标…...

JDK介绍
JDK是Java Development Kit的缩写,是Oracle公司提供的用于开发Java应用程序的开发包。它包括了Java运行环境(Java Runtime Environment),以及一系列Java工具(如javac、java、jdb等)和Java基础类库ÿ…...

JavaScript 对象入门:基础用法全解析
目录 对象 语法 属性和访问 方法和调用 this关键字 null 遍历对象 内置对象 Math 属性 方法 Date 创建日期对象 获取和设置日期 ⭐对象 对象是 JavaScript 数据类型的一种,数据类型也包括数值类型、字符串类型、布尔类型、undefined。对象数据类型可…...
如何获得一个Oracle 23ai数据库(docker容器)
准确的说,是Oracle 23ai Free Developer版,因为企业版目前只在云上(OCI和Azure)和ECC上提供。 方法包括3种,本文介绍第3种: Virtual ApplianceRPM安装Docker 我已经有了一台Oracle Linux 8的虚机&#x…...

想跨境出海?云手机提供了一种可能性
全球化时代,越来越多的中国电商开始将目光投向了海外市场。这并不是偶然,而是他们在长期的市场运营中,看到了出海的必要性和潜在的机会。 中国的电商市场无疑是全球最大也最发达的之一。然而,随着市场的不断发展和竞争的日益加剧…...

制药行业新突破:CANOpen转PROFINET网关配置案例解析
在药品制造工业环境中,实现CanOpen转Profinet协议之间转换的网关配置是一个关键过程,确保了不同通信协议的设备能够互相协作。以开疆智能CanOpen转Profinet网关为例,以下是其配置流程:首先安装CanOpen转Profinet网关的配置软件&am…...

vue前端时间段选择控件
实现效果: 可选具体的某天的某时某分某秒 vue前端代码: <el-form-item label"日期"><el-date-pickerv-model"daterangerq"style"width: 240px"value-format"yyyy-MM-dd HH:mm:ss"type"datetimerange"range-separat…...
用wordpress建外贸独立站的是主流的外贸建站方式
WordPress因其易用性、灵活性和强大的功能支持,成为了外贸企业首选的网站建设平台。 从技术和功能角度来看,WordPress提供了丰富的主题和插件,这些都是构建专业外贸网站所必需的。例如,有专门为外贸网站设计的主题和插件…...

差异基因散点图绘制教程
差异基因散点图绘制教程 本期教程 小杜的生信笔记,自2021年11月开始做的知识分享,主要内容是R语言绘图教程、转录组上游分析、转录组下游分析等内容。凡事在社群同学,可免费获得自2021年11月份至今全部教程,教程配备事例数据和相…...
Windows安装多版本MySQL
5.7 - 配置 my.ini [client] port 3307[mysql] default-character-set utf8mb4[mysqld] port 3307 server_id 1 character-set-server utf8mb4basedir "E://MySQL/mysql5.7" datadir "E://MySQL/mysql5.7/data"# 将所有表名转换为小写字母 lower_c…...
Redis7降级到Redis6如何AOF备份恢复(错的)
Redis7降级到Redis6如何AOF备份恢复(错的) 前提:从始至终开启AOF 介绍的Docker安装的Redis,不是Docker也一样,差不多 一、data目录差异 redis版本7 - /data/appendonlydir - appendonly.aof.manifest - appendo…...

通过EXCEL控制PLC启停电机的一种方法
概述 本例将介绍用微软EXCEL电子表格控制西门子S7-1200 PLC实现电机启停的一种方法。 第1步: 添加PLC设备,选择西门子S7-1214C CPU,设置IP地址:192.168.18.18,子网掩码:255.255.255.0。 第2步:…...

【GPT4O 开启多模态新时代!】
系列文章目录 GPT-4o的出现,让 AI 真正进入了全能时代,而且 OpenAI 宣布所有人免费使用! 不论你是需要写文章、听声音还是看视频,GPT-4o都能满足你的需求 文章目录 系列文章目录什么是GPT-4o?一、GPT-40 的新功能二、…...
HTTP协议及Python实现
最近的项目需要频繁在前后端之间传输数据,本篇主要介绍HTTP协议以及数据传输方法。 1 HTTP协议 1.1 http协议简介 HTTP(Hypertext Transfer Protocol)是一种用于传输超文本数据的应用层协议。它是万维网上数据交换的基础,定义了客户端和服务器之间进行通…...
【机器学习】逻辑化讲清PCA主成分分析
碎碎念:小编去年数学建模比赛的时候真的理解不了主成分分析中的“主成分”的概念!!但是,时隔两年,在机器学习领域我又行了,终于搞明白了!且看正文!再分享一个今天听到的播客中非常触…...
Vue常见的指令
Vue.js 提供了许多内置指令,这些指令可以在模板中用于处理元素的显示、行为等。以下是 Vue.js 中常见的 7 个指令及其详细代码示例: 1、v-bind:用于属性绑定,可以动态更新 HTML 属性。 html<template> <div> <img…...

【Ansible】ansible-playbook剧本
playbook 是ansible的脚本 playbook的组成 1)Tasks:任务;通过tasks 调用ansible 的模板将多个操作组织在一个playbook中运行 2)Variables:变量 3)Templates:模板 4)Handles…...

Linux的命令
; 昨天学习了七个命令,分别是:cd命令(切换目录)、pwd命令(当前目录)、mkdir命令(创建目录)、touch命令(创建文件)、date命令(显…...
No known conditions for “./lib/locale/lang/zh-cn“ entry in “element-plus“ pa
yarn的安装和卸载 npm install -g yarn npm uninstall yarn -g //yarn卸载 改用yarn卸载试试 先安装yarn npm install -g yarn 卸载掉原来的element-plus yarn remove element-plus 重新安装原有的element-plus版本 yarn add element-plus2.3.1 低版本页面引用为 i…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...