news 2026/6/20 13:52:40

从零手搓实现 Linux 简易 Shell:内建命令 + 环境变量 + 程序替换全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零手搓实现 Linux 简易 Shell:内建命令 + 环境变量 + 程序替换全解析

从零手搓实现 Linux 简易 Shell
(内建命令 + 环境变量 + 程序替换完整版)

下面我们用 C 语言一步步实现一个极简但功能相对完整的 shell,支持:

  • 读取用户输入、解析命令行(支持参数)
  • 内建命令(cd、exit、pwd、echo、export)
  • 环境变量的读取与修改(支持P A T H 、 PATH、PATHHOME 等)
  • 通过 fork + execvp 执行外部程序
  • 管道(|)的简单支持(单级管道)
  • 前后台进程(&)
  • 基本的信号处理(Ctrl+C 不退出 shell)

目标最终效果(类似 bash 的极简版)

myshell$pwd/home/user myshell$cd/tmp myshell$echo$HOME/home/user myshell$exportMYVAR=hello myshell$echo$MYVARhello myshell$ls-l|greptxt -rw-r--r--1user user1234Feb4txtfile.txt myshell$sleep10&[1]12345myshell$

完整实现代码(约 300 行)

#define_GNU_SOURCE#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/wait.h>#include<sys/types.h>#include<signal.h>#include<errno.h>#include<limits.h>#defineMAX_INPUT1024#defineMAX_ARGS64#defineMAX_PIPES2// 支持一级管道// 全局环境变量表(我们自己维护一份副本)externchar**environ;char**my_environ=NULL;// 前台进程组 pidpid_tfg_pgid=0;// ---------------------- 辅助函数 ----------------------// 字符串 trim 两端空白voidtrim(char*str){char*start=str;char*end;// 跳过开头空白while(*start&&(*start==' '||*start=='\t'||*start=='\n'))start++;if(*start==0){*str='\0';return;}// 找到结尾end=start+strlen(start)-1;while(end>start&&(*end==' '||*end=='\t'||*end=='\n'))end--;*(end+1)='\0';// 移动到开头if(start!=str)memmove(str,start,end-start+2);}// 解析一行输入为参数数组intparse_command(char*input,char**args){intargc=0;char*token;trim(input);if(strlen(input)==0)return0;token=strtok(input," \t\n");while(token&&argc<MAX_ARGS-1){args[argc++]=token;token=strtok(NULL," \t\n");}args[argc]=NULL;returnargc;}// 查找命令是否为内建intis_builtin(constchar*cmd){returnstrcmp(cmd,"cd")==0||strcmp(cmd,"exit")==0||strcmp(cmd,"pwd")==0||strcmp(cmd,"echo")==0||strcmp(cmd,"export")==0||strcmp(cmd,"env")==0;}// 内建命令执行intbuiltin_execute(char**args){if(!args[0])return1;if(strcmp(args[0],"cd")==0){if(!args[1]){chdir(getenv("HOME"));}else{if(chdir(args[1])!=0){perror("cd");}}return1;}if(strcmp(args[0],"exit")==0){exit(0);}if(strcmp(args[0],"pwd")==0){charcwd[PATH_MAX];if(getcwd(cwd,sizeof(cwd))!=NULL){printf("%s\n",cwd);}else{perror("pwd");}return1;}if(strcmp(args[0],"echo")==0){inti=1;while(args[i]){printf("%s",args[i]);if(args[i+1])printf(" ");i++;}printf("\n");return1;}if(strcmp(args[0],"export")==0){if(args[1]){// 支持 export VAR=value 或 export VARchar*eq=strchr(args[1],'=');if(eq){*eq='\0';setenv(args[1],eq+1,1);}else{setenv(args[1],"",1);}}return1;}if(strcmp(args[0],"env")==0){char**env=environ;while(*env){printf("%s\n",*env);env++;}return1;}return0;}// 简单信号处理:Ctrl+C 只影响前台进程voidsigint_handler(intsig){if(fg_pgid>0){kill(-fg_pgid,SIGINT);}// 不退出 shell}// 执行外部命令(支持单级管道)voidexecute_external(char**args,intbackground){intpipefd[2];inthas_pipe=0;// 检测是否有管道for(inti=0;args[i];i++){if(strcmp(args[i],"|")==0){has_pipe=1;args[i]=NULL;// 分割成两个命令char**cmd2=&args[i+1];pipe(pipefd);pid_tpid1=fork();if(pid1==0){// 左命令dup2(pipefd[1],STDOUT_FILENO);close(pipefd[0]);close(pipefd[1]);execvp(args[0],args);perror("execvp left");_exit(1);}pid_tpid2=fork();if(pid2==0){// 右命令dup2(pipefd[0],STDIN_FILENO);close(pipefd[0]);close(pipefd[1]);execvp(cmd2[0],cmd2);perror("execvp right");_exit(1);}close(pipefd[0]);close(pipefd[1]);if(!background){fg_pgid=pid1;waitpid(pid1,NULL,0);waitpid(pid2,NULL,0);fg_pgid=0;}else{printf("[%d] %d\n",1,pid1);}return;}}// 无管道,普通执行pid_tpid=fork();if(pid==0){// 子进程execvp(args[0],args);perror("execvp");_exit(1);}elseif(pid>0){if(!background){fg_pgid=pid;waitpid(pid,NULL,0);fg_pgid=0;}else{printf("[%d] %d\n",1,pid);}}}// 主循环intmain(){charinput[MAX_INPUT];char*args[MAX_ARGS];intargc;// 初始化环境变量副本(可选,setenv/putenv 实际操作的是 environ)// my_environ = environ; // 如果需要自己维护可复制signal(SIGINT,sigint_handler);signal(SIGTSTP,SIG_IGN);// 忽略 Ctrl+Zprintf("欢迎使用 myshell (简易版)\n");printf("支持命令:cd, pwd, echo, export, exit, env, 外部程序, 简单管道, & 后台\n\n");while(1){charcwd[PATH_MAX];getcwd(cwd,sizeof(cwd));printf("myshell:%s$ ",strrchr(cwd,'/')?strrchr(cwd,'/'):cwd);if(!fgets(input,MAX_INPUT,stdin)){printf("\n退出\n");break;}// 去掉换行input[strcspn(input,"\n")]=0;// 解析argc=parse_command(input,args);if(argc==0)continue;intbackground=0;if(argc>1&&strcmp(args[argc-1],"&")==0){background=1;args[--argc]=NULL;}// 内建命令if(is_builtin(args[0])){builtin_execute(args);continue;}// 外部命令或管道execute_external(args,background);}return0;}

编译 & 运行

gcc -o myshell myshell.c ./myshell

功能说明与扩展点

已实现:

  • 内建:cd、exit、pwd、echo、export、env
  • 环境变量读取($PATH 等通过 getenv)
  • 外部程序执行(execvp)
  • 简单单级管道(ls | grep)
  • 后台执行(sleep 100 &)
  • Ctrl+C 只杀前台进程

可以继续扩展的方向

  1. 支持多级管道(需要多个 pipefd 数组 + 循环 fork)
  2. 支持重定向(< > >>)→ 解析时识别符号,dup2
  3. 支持环境变量展开($VAR)→ 在 parse 前替换
  4. 支持历史命令(上下箭头)→ readline 库
  5. 支持 job control(jobs、fg、bg)→ 记录后台进程列表
  6. 支持 alias

小结

这个版本大约 300 行代码,已经包含了 shell 最核心的三大能力:

  • 内建命令(直接执行)
  • 环境变量(getenv / setenv)
  • 程序替换(fork + execvp)

如果你想继续深入某个部分(例如:实现多级管道、重定向、变量展开、job control),可以告诉我,我可以继续补充对应代码和解析。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 1:16:12

Linux 进程创建与终止全解析:fork 原理 + 退出机制实战

Linux 进程创建与终止全解析&#xff1a;fork 原理 退出机制实战 Linux 中进程创建和终止是操作系统最核心、最基础的行为之一&#xff0c;理解清楚这两个过程&#xff0c;对理解进程管理、资源分配、僵尸进程、孤儿进程、wait/waitpid、信号处理等都至关重要。 本文将从原理…

作者头像 李华
网站建设 2026/5/30 17:45:46

熬夜整理了100+可视化大屏模板,直接下载就能用!

a目录 一、颜值即正义&#xff0c;风格全覆盖 二、行业全覆盖&#xff0c;贴合业务场景 1、智慧城市 / 政务监管 2、智慧工厂 / 车间监控 3. 电商 / 流量分析 4. 金融 / 物流 / 医疗等更多场景 三、框架完整&#xff0c;开箱即用 &#x1f3af; 这些人一定要冲&#xf…

作者头像 李华
网站建设 2026/6/15 19:15:42

AI写论文大揭秘!这4款AI论文生成工具,让你写职称论文如鱼得水!

在2025年&#xff0c;学术写作正处于智能化的浪潮之中&#xff0c;越来越多的研究者开始依赖AI写论文工具。许多现有的工具在撰写硕士、博士论文等长篇学术作品时&#xff0c;常常面临理论深度不足或逻辑不严谨的问题。普通的AI论文写作工具根本无法满足专业论文创作的需求。 …

作者头像 李华
网站建设 2026/5/28 12:14:51

星载AI的生死考场:构建辐射故障注入仿真平台的三重架构

随着低轨星座进入万星时代&#xff0c;宇宙射线引发的单粒子翻转(SEU)已成为星载AI软件的头号杀手。传统地面测试无法复现太空辐射环境&#xff0c;而基于云端AI的故障注入平台正重构测试范式。 一、仿真平台核心架构解析 1. 数字孪生层&#xff1a;高保真太空战场 多物理场耦…

作者头像 李华
网站建设 2026/6/6 8:11:25

虫洞通信仿真:量子纠缠分发在深空延迟的可靠性测试工具——面向软件测试工程师的技术实践指南

一、工具设计背景&#xff1a;量子通信的测试挑战 深空通信的延迟瓶颈 传统射频通信在星际传输中存在分钟级延迟&#xff08;如地火通信单向延迟达4-24分钟&#xff09;&#xff0c;而量子纠缠分发理论上可实现瞬时信息传递。但宏观虫洞模型中的量子纠缠稳定性受距离、介质干扰…

作者头像 李华
网站建设 2026/6/18 20:20:02

DNA存储数据高温降解率测试工具:软件测试从业者的专业解析

在生物计算领域&#xff0c;DNA存储技术凭借其超高密度和长效性成为数据存储的新兴方案&#xff0c;但高温环境下的DNA降解问题一直是关键挑战。本文从软件测试从业者的角度&#xff0c;解析DNA降解率测试工具的技术原理、验证方法&#xff0c;并结合2026年公众号内容热度趋势&…

作者头像 李华