当前位置: 首页 > news >正文

【Linux】进程控制(4)自主shell命令行解释器

【Linux】进程控制(4)自主shell命令行解释器

这一部分的目标是:自己动手写一个极简的 shell,通过这个过程把之前学过的进程控制知识(fork/exec/wait/signal/管道/重定向等)串联起来。

目标 shell 的功能范围(极简版)

  • 显示提示符(如myshell$
  • 读取一行用户输入
  • 支持最基本的命令执行(不带参数也可以先支持)
  • 支持带参数的命令(空格分隔)
  • 支持后台运行(命令末尾有&
  • 支持前台命令的 wait
  • 支持 exit / quit 退出
  • (可选扩展)支持管道|、重定向>>><&&||

实现思路总览

while (true) { 打印提示符 读取整行输入 → line 如果 line 为空 或 是 exit/quit → break 解析 line → 切分出命令 + 参数数组 argv[] → 判断是否有 & (后台) if (是内置命令,如 cd exit) { 直接在当前进程执行 } else { pid = fork() if (pid == 0) { // 子进程 execvp(命令, argv) perror("exec失败"); exit(1); } else { // 父进程(shell) if (不是后台) { waitpid(pid, &status, 0); } else { printf("[后台任务] pid = %d\n", pid); // 可以选择不 wait,等它自己结束或用 waitpid(-1,...) 回收 } } } }

极简版代码(推荐先实现这个)

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/wait.h>#include<errno.h>#defineMAX_LINE1024#defineMAX_ARGS64intmain(void){charline[MAX_LINE];char*argv[MAX_ARGS];charprompt[]="myshell$ ";while(1){printf("%s",prompt);fflush(stdout);// 读取整行if(!fgets(line,MAX_LINE,stdin)){break;// ctrl+D}// 去掉末尾换行line[strcspn(line,"\n")]=0;// 空行直接继续if(strlen(line)==0)continue;// 退出命令if(strcmp(line,"exit")==0||strcmp(line,"quit")==0){break;}// 解析参数intargc=0;char*token=strtok(line," \t");intbackground=0;while(token){if(strcmp(token,"&")==0){background=1;break;// & 后面不再解析}argv[argc++]=token;token=strtok(NULL," \t");}argv[argc]=NULL;if(argc==0)continue;// 内置命令(先简单处理 exit/cd)if(strcmp(argv[0],"cd")==0){if(argc<2){fprintf(stderr,"cd: 缺少参数\n");}elseif(chdir(argv[1])!=0){perror("chdir");}continue;}// fork + execpid_tpid=fork();if(pid<0){perror("fork");continue;}if(pid==0){// 子进程execvp(argv[0],argv);// 走到这里说明 exec 失败fprintf(stderr,"命令 '%s' 执行失败: %s\n",argv[0],strerror(errno));exit(127);}// 父进程if(!background){intstatus;waitpid(pid,&status,0);}else{printf("[后台作业] pid = %d\n",pid);// 可以不 wait,让它成为孤儿进程,由 init 回收// 或者后续用 waitpid(-1, ...) 非阻塞回收}}printf("\nbye~\n");return0;}

编译 & 运行

gcc -o myshell myshell.c ./myshell

然后就可以输入:

myshell$ ls -l myshell$ sleep 10 & myshell$ pwd myshell$ cd /tmp myshell$ exit

进阶功能(按难度递增)

优先级功能关键实现点难度
★☆☆支持管道 `cmd1cmd2`fork 两次 + pipe() + dup2
★★☆支持重定向> >> <解析 > >> <,用 open() + dup2★★★
★★☆回收所有后台进程(waitpid)非阻塞 waitpid(-1, &status, WNOHANG) 循环★★
★★★支持&&``
★★★实现!历史命令保存历史链表,按 !n 执行★★★
★★★★支持环境变量展开$HOME解析 $ 开头的词,用 getenv() 替换★★★★
★★★★支持通配符 * ?用 glob() 或自己实现匹配★★★★★

推荐进阶顺序(比较实用)

  1. 先把上面的极简版跑通
  2. 加上后台进程的非阻塞回收(每轮循环末尾试一次 waitpid(-1, …, WNOHANG))
  3. 实现最简单的重定向(只支持><
  4. 再实现单级管道(最有成就感)

小提示

  • 解析命令行时建议用strtok_r()(可重入版本)更安全
  • 处理输入时考虑连续空格、tab、行首尾空格
  • execvp() 失败时退出码常用 127(command not found)
  • 后台进程结束时如果不回收,会出现僵尸进程(zombie)

如果你现在想继续做这个项目,可以告诉我你想先实现哪个功能(管道、重定向、后台回收、历史命令……),我可以给你对应的核心代码片段和注意事项。

祝你写出一个属于自己的 shell~

http://www.jsqmd.com/news/336650/

相关文章:

  • 计算机毕业设计之jsp学院实云教室后台管理模块验室计算机使用情况统计系统
  • 2026年 模块化机房/精密配电柜/一体化机柜/精密空调/机房精密空调厂家推荐榜:专业智造与高效节能方案深度解析 - 品牌企业推荐师(官方)
  • Day28环境对象与回调函数
  • 为什么许多原本的 Java 项目都试图用 go 进行重写开源?
  • pore-pressure diffusion and poroelastic stress changes
  • 办公久坐大肚腩怎么减?腰纪线(MetaSlim)全营养代餐粉:以代谢为核心,重塑轻盈体态 - 品牌企业推荐师(官方)
  • 智能安全防护:吹风机超时自动断电保护插座的设计与实现
  • 90%前端面试必问的12个JS核心,搞懂这些直接起飞!
  • 云服务器ECS-高性能弹性计算服务-99.995%可用性保障
  • 计算机毕业设计之基于Spring boot食品安全信息管理系统
  • 星际航行时代,汉语凭什么成为文明的“时间锚点”?
  • 计算机毕业设计之php-基于PHP的火锅店点餐系统
  • 收藏必看!Kimi K2.5技术深度解析:文本视觉融合+智能体集群+视频理解三大突破
  • 服务器核心功能与应用场景全解析
  • Podman怎么设置开机自启动
  • 卸载验证:AI驱动痛点破解,测试从业者从成本中心到价值引擎
  • 计算机毕业设计之基于SSM的社区疫情管理系统的设计与实现
  • 无人值守场景下,如何构建高可用的温湿度监控系统?这4个设计要点缺一不可
  • <span class=“js_title_inner“>2025 年度语言:C#</span>
  • Apache Jena:利用 SPARQL 查询与推理机深度挖掘知识图谱 - 教程
  • 隧道调频广播覆盖系统:隧道无线广播技术赋能行车安全升级
  • 收藏备用|程序员/小白入门大模型不踩坑!转行+学习全攻略
  • 学术“黑科技”揭秘:书匠策AI如何让期刊论文写作变身“开挂游戏”
  • 收藏!小白/程序员必看:非开发岗想冲大模型?别瞎想(避坑指南)
  • 金属基板常见失效模式分析与可靠性设计改进
  • <span class=“js_title_inner“>面了一个75k的字节小姐姐,想当场给她offer。</span>
  • Word侧边页码设置全攻略
  • <span class=“js_title_inner“>一个提示词,把文章提炼出成卡通风信息图</span>
  • 不同应用场景下金属基板选型与方案配置指南
  • 收藏级锐评!从夯到拉,一文读懂大模型岗位(小白/程序员必看)