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

Letter Shell:问题修复与功能扩展 - EM

仓库:https://github.com/NevermindZZT/letter-shell

前言

在之前的文章中,我介绍了 Letter Shell 的基本使用方法;在实际使用过程中,我发现 Letter Shell 存在一些小问题,也结合项目需求对其功能进行了扩展;本文将介绍这些问题的现象与修复方法,并分享新增功能的实现过程和实际用途;

问题修复(部分修复)

现象:在 Letter Shell 中,串口输入时长按退格键或 Delete 键时,终端显示存在字符残留,虽然字符已无法继续删除,但视觉上仍未完全清除;

这背后的常见原因:

  1. 终端行为:

    • 串口终端(如 SecureCRT、TeraTerm、MobaXterm 等)长按退格时会快速发出多个 0x08 字节;
    • 这些字符本质是 “光标后退一格”,但 Letter Shell 要负责同步显示以及缓存处理;
  2. Letter Shell 内部处理问题:

    • Letter Shell 会维护一个输入缓存;
    • 如果没有正确处理 “删到头” 的情况,可能会出现:
      • 显示没擦干净(字符残留);
      • 缓存指针已到头,仍在处理退格;
      • 没有回显正确的光标移动和空格清除字符;

修复方法要点

  1. 判断输入缓存是否为空:在 shellHandler() 中处理退格符(0x08)时,判断光标是否已在开头,防止“负向删除”(即光标越界到输入行左边界之外);

  2. 对于连续退格,要保证:

    • Shell 缓冲区更新正确;
    • 串口输出内容与终端光标同步;
    • 不要超出缓存边界;

修改后的完整代码

此函数整体思路是:先调整缓存,再清空并重绘整行命令,最后将光标还原至正确位置;

void shellDeleteByte(Shell *shell, signed char direction)
{char offset = (direction == -1) ? 1 : 0;if ((shell->parser.cursor == 0 && direction == 1)|| (shell->parser.cursor == shell->parser.length && direction == -1)){return;}if (shell->parser.cursor == shell->parser.length && direction == 1){shell->parser.cursor--;shell->parser.length--;shell->parser.buffer[shell->parser.length] = 0;shellDeleteCommandLine(shell, 1);}else{for (short i = offset; i < shell->parser.length - shell->parser.cursor; i++){shell->parser.buffer[shell->parser.cursor + i - 1] = shell->parser.buffer[shell->parser.cursor + i];}shell->parser.length--;if (!offset){shell->parser.cursor--;shellWriteByte(shell, '\b');}shell->parser.buffer[shell->parser.length] = 0;// 将光标移动到行首for (short i = 0; i < shell->parser.cursor + 1; i++){shellWriteByte(shell, '\b');}// 重新打印整个命令行for (short i = 0; i < shell->parser.length; i++){shellWriteByte(shell, shell->parser.buffer[i]);}// 打印一个空格,覆盖掉原来的最后一个字符shellWriteByte(shell, ' ');// 将光标移动到正确的位置for (short i = shell->parser.length; i > shell->parser.cursor; i--){shellWriteByte(shell, '\b');}}
}

该函数通过整体刷新命令行缓冲区,并手动控制光标移动,确保删除行为与终端显示保持一致,尤其适用于字符中间删除的情形;
不过在实际使用中,仍会在某些输入场景下出现字符未被正确清除的情况,说明该方案不够完全可靠;如果你对此有更优的实现思路,欢迎联系我交流(联系方式已在主页提供);

功能扩展

在 Linux Shell 中,如果你在终端中键入了一长串命令,但突然不想执行,也不想一个一个退格删除,只需要轻轻按下 Ctrl+C,整行输入就会被取消,重新回到提示符;这种“强制销毁当前输入”的行为,对嵌入式调试来说也非常实用;
Letter Shell 默认的 Ctrl+C 实现主要用于中断长时间运行的命令,但并不具备清除当前行的效果;接下来,我们就来实现这一功能;

实现思路

  1. 使用 SHELL_EXPORT_KEY 注册一个 Ctrl+C 快捷键;
  2. 在回调函数中:
    • 将 is_CtrlC_Pressed 标记设为 1(供其他任务判断是否中断);
    • 重新输出提示符(myshellWritePrompt);
    • 清空当前行缓存(myshellExec 直接执行空行);
  3. 由于 Letter Shell 的内部函数是 static 的,我们自行实现 myshellWritePrompt 和 myshellExec 两个版本;

Ctrl+C 注册代码(port 文件中)

/* Ctrl+C 注册,键值为 0x03 */
uint8_t is_CtrlC_Pressed = 0;static void CtrlC_Interrupt(Shell* shell)
{// 设置中断标志位,可供其他长运行命令判断is_CtrlC_Pressed = 1;extern void myshellWritePrompt(Shell *shell, unsigned char newline);myshellWritePrompt(shell, 1); // 输出新的一行提示符extern void myshellExec(Shell *shell);myshellExec(shell); // 清除当前行输入
}SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x03000000, CtrlC_Interrupt, Ctrl_C);

添加 myshellWritePrompt 与 myshellExec 函数(shell.c)

由于原函数是 static,我们避免直接修改 Letter Shell 源码,而是新建两个函数:

myshellWritePrompt:打印提示符

void myshellWritePrompt(Shell *shell, unsigned char newline)
{if (shell->status.isChecked){if (newline){shellWriteString(shell, "\r\n");}shellWriteString(shell, shell->info.user->data.user.name);shellWriteString(shell, ":");shellWriteString(shell, shell->info.path ? shell->info.path : "/");shellWriteString(shell, "$ ");}else{shellWriteString(shell, shellText[SHELL_TEXT_PASSWORD_HINT]);}
}

myshellExec:清空当前命令缓冲区

void myshellExec(Shell *shell)
{if (shell->parser.length == 0){return;}shell->parser.buffer[shell->parser.length] = 0;if (shell->status.isChecked){// 此处不添加到历史、不调用命令,仅清空缓冲区shellParserParam(shell); shell->parser.length = shell->parser.cursor = 0;if (shell->parser.paramCount == 0){return;}}else{shellCheckPassword(shell);}
}

注意:该功能依赖串口工具正确发送 0x03(Ctrl+C)字符,建议使用支持该行为的终端,如 MobaXterm、TeraTerm 等;

扩展建议:基于 is_CtrlC_Pressed 的中断任务设计

通过 is_CtrlC_Pressed 标志,用户可以在长时间运行的命令中主动检查该变量,实现软中断功能;例如:

while (running) {if (is_CtrlC_Pressed) {break;}do_some_heavy_task();
}

注意:此中断机制适用于循环任务或长延时任务的主动中断,不适用于系统层级的任务终止;

总结

本文基于 Letter Shell 在实际嵌入式项目中的使用经验,修复了输入过程中因退格处理不完整导致的字符残留问题,并新增 Ctrl+C 清除当前输入行的功能,提升了交互体验与调试效率;

在功能扩展的同时,尽量保持对原始源码的非侵入性,便于后续升级与维护;Letter Shell 作为一款轻量级命令行工具,其灵活的可扩展性值得深入挖掘;

如果你也在使用它,欢迎交流更多改进思路;

阅读原文:Letter Shell:问题修复与功能扩展

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

相关文章:

  • amlogic-s9xxx-armbian项目深度解析:全志H6机顶盒网络适配终极指南
  • 终极指南:3分钟掌握网易云音乐NCM文件解密转换
  • Windows系统优化神器:WinUtil如何用5分钟重塑你的电脑体验?
  • 利用taotoken实现aigc内容创作平台的模型降本与调度
  • 抖音不能下载的视频怎么保存到相册?最新方法攻略 - 爱上科技热点
  • 如何3步突破AI编辑器限制:跨平台智能标识重置完整指南
  • 2026 年 5 月兰州宝宝照 / 百天照评测,四大靠谱门店排行推荐 - 生活测评君
  • 从游戏道具到建筑外墙:3ds Max多维子材质(Multi/Sub-object)实战应用拆解,附避坑指南
  • 做针头检测仪的朋友,这款屏适配性拉满✨ - 浴缸里的巡洋舰
  • 【含五月份最新安装包】OpenClaw 2.6.6 飞书接入|机器人配置全流程
  • 2026届学术党必备的十大降AI率助手推荐
  • KMS智能激活工具:彻底告别Windows和Office激活烦恼的终极解决方案
  • 在 Taotoken 模型广场中根据任务与预算进行模型选型的直观体验
  • 【限时首发】.NET 9容器配置安全白皮书:3类高危配置泄露路径+OWASP Top 10容器适配方案
  • Atombot:500行代码构建个人AI助手,模块化设计实现本地化智能
  • 告别拥堵!用MINCO算法为无人机集群规划“空中立交桥”(附避障实战代码)
  • 一年做座舱,我才搞懂这10件事(全是血泪)
  • fre:ac音频转换器终极指南:免费开源的多功能音频处理解决方案
  • 如何用Battery Toolkit彻底解决MacBook电池焦虑:Apple Silicon用户的终极指南
  • 2026年3月QJ型水泵厂商推荐,热水泵/高压潜水泵/天津水泵/300QJ型水泵/矿用高压泵,QJ型水泵公司哪家强 - 品牌推荐师
  • 魔兽争霸3终极兼容解决方案:WarcraftHelper完整配置指南
  • 别再为坐标系头疼了!一文彻底搞懂Nuscenes与KITTI的3D标注差异(附转换核心代码解析)
  • 如何通过本地解析技术实现九大网盘文件高速下载
  • QKeyMapper:Windows平台高级输入设备映射引擎的技术架构与性能优化
  • 避坑指南:在Windows老电脑/无独显环境下跑通OpenAI Whisper语音转文字(CPU模式详解)
  • 【含五月最新安装包】OpenClaw 2.6.6 Win11 专属教程|AI 电脑操控配置指南
  • Letter Shell:自定义函数参数解析 - EM
  • 如何在GitHub上优雅显示数学公式:MathJax插件的专业解决方案
  • 3分钟轻松汉化Axure RP:告别英文界面的完整中文语言包指南
  • OpenDify全栈AI平台:从零部署私有化知识库与智能工作流