🧩 项目介绍

该项目使用 Node.js 实现了一个模拟的 Linux 终端环境,支持多种常见的 Linux 命令(如 ls, cd, cat, mkdir, rm 等),所有文件操作都在内存中进行,并持久化到本地文件系统中。适合用于学习 Shell 命令实现原理、文件系统结构或作为教学演示工具。


📦 依赖安装

确保你已安装 Node.js(建议版本 14+),然后运行以下命令安装依赖:

npm install

🚀 启动项目

在项目根目录下运行:

node index.js

你会看到命令提示符:

simu-shell:~$ _

此时你可以输入 Linux 命令进行操作。


📚 支持命令列表

以下是你可以在模拟终端中使用的命令及其基本用法说明:

命令用法示例功能说明
helphelp显示所有可用命令
exitexit退出模拟终端
clearclear清空终端屏幕
historyhistory查看历史命令
pwdpwd显示当前路径
lsls列出当前目录下的文件
llll显示当前目录下的详细文件信息(带类型、大小、修改时间等)
cdcd /home/user切换目录
mkdirmkdir newdir创建目录
rmdirrmdir emptydir删除空目录
rmrm file.txt
rm -r dir
删除文件或目录(递归)
touchtouch newfile.txt创建空文件
echoecho "Hello" > file.txt将字符串写入文件
catcat file.txt查看文件内容
cpcp src.txt dest.txt复制文件或目录
mvmv oldname.txt newname.txt移动或重命名文件/目录
headhead file.txt
head -n 5 file.txt
查看文件前几行
tailtail file.txt
tail -n 5 file.txt
查看文件最后几行
grepgrep "hello" file.txt在文件中查找字符串
findfind file.txt查找文件
statstat file.txt显示文件或目录的详细信息
vimvim file.txt编辑文件
yumyum install https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.26-winx64.zip下载文件
zipzip -r archive.zip test.txt压缩文件
unzipunzip archive.zip -d unzip_target解压文件
rzrz a.txt上传文件
szsz a.txt下载文件

💡 示例操作流程

你可以在模拟终端中依次执行以下命令来测试功能:

help
ls
mkdir test
cd test
touch file.txt
echo "Hello World" > file.txt
cat file.txt
cp file.txt copy.txt
ls
mv copy.txt renamed.txt
cat renamed.txt
rm renamed.txt
ls
cd ..
rm -r test

🧪 测试脚本

项目中提供了 test.md 文件,里面包含了完整的测试命令集,建议你在终端中逐步运行测试命令以验证所有功能是否正常。


🧱 数据持久化机制

项目使用了内存中的虚拟文件系统(VFS),并通过以下方式持久化:

  • 文件内容保存在 storage/files/ 目录下,使用 UUID 命名;
  • 文件系统结构保存在 vfs_data.json 中,每次操作后会自动保存。

📁 项目结构说明

nodejs模拟Linux环境/
├── index.js                  # 主程序入口
├── shell.js                  # 命令处理核心逻辑
├── commands/                 # 各命令的实现
├── vfs.js                    # 虚拟文件系统核心
├── storage.js                # 文件系统结构的持久化
├── fileStorage.js            # 文件内容的持久化
├── utils.js                  # 工具函数(如引号解析)
├── test.md                   # 测试命令列表
├── README.md                 # 本文件
└── package.json              # 项目依赖配置

✅ 项目特点

  • 🧠 使用纯 Node.js 实现,无需依赖外部库(除 uuid);
  • 💾 支持数据持久化,重启后可恢复文件系统状态;
  • 📚 支持大多数常见 Linux 命令;
  • 🛠️ 结构清晰,便于扩展新命令或修改现有逻辑;
  • 🧪 提供完整测试用例,方便验证功能。

📎 扩展建议

你可以根据需要扩展以下功能:

  • 添加新的命令(如 chmod, chmod, grep -r);
  • 支持管道(|)和重定向(>>, <);
  • 支持用户权限管理;
  • 添加命令自动补全;
  • 添加图形化界面(Electron);
  • 支持多用户系统。

源码下载

Node.js 模拟 Linux 环境

核心代码

tool/index.js

// index.js
const readline = require('readline');
const { processCommand } = require('./shell');
const { loadHistory, saveHistory } = require('./historyStorage');const rl = readline.createInterface({input: process.stdin,output: process.stdout,prompt: 'simu-shell:~$ ',
});// 启动时清屏
process.stdout.write('\x1B[2J\x1B[0f');rl.history = loadHistory();
rl.historySize = 100;rl.prompt();rl.on('line', (line) => {const trimmed = line.trim();processCommand(trimmed, rl, () => {rl.prompt();});
}).on('close', () => {saveHistory(rl.history);console.log('\n退出模拟终端');process.exit(0);
});

tool/vfs.js

// vfs.jsconst path = require('path');const fs = require('./storage').load({'/': {type: 'dir',children: {home: {type: 'dir',children: {user: {type: 'dir',children: {'file.txt': { type: 'file', content: 'Hello World' },'notes.md': { type: 'file', content: '# My Notes' }}}}},bin: {type: 'dir',children: {}}}}
});const storage = require('./storage');// 每次修改后自动保存
function persist() {storage.save(fs);
}// 提供一个统一的写入接口
function updateFilesystem(mutateFn) {mutateFn(fs);persist();
}function readdir(path, callback) {const parts = path.split('/').filter(p => p !== '');let current = fs['/'];for (let part of parts) {if (current && current.type === 'dir' && current.children[part]) {current = current.children[part];} else {return callback(`找不到目录: ${path}`);}}callback(null, Object.keys(current.children));
}function chdir(path, currentDir, callback) {const resolvedPath = resolvePath(path, currentDir);const parts = resolvedPath.split('/').filter(p => p !== '');let current = fs['/'];for (let part of parts) {if (current && current.type === 'dir' && current.children[part]) {current = current.children[part];} else {return callback(`找不到目录: ${resolvedPath}`);}}callback(null, resolvedPath);
}function resolvePath(path, currentDir) {if (path.startsWith('/')) return path;if (currentDir === "/") return normalizePath(`/${path}`);return normalizePath(`${currentDir}/${path}`);
}function normalizePath(inputPath) {// 使用 path.normalize 解析 .. 等相对路径let normalized = path.normalize(inputPath).replace(/^(\.\.\/|\/)?/, '')  // 移除开头的 ./ ../ /.replace(/\\/g, '/');          // 统一为正斜杠if (normalized.startsWith("/")) {return normalized;}return '/' + normalized;
}function getNodeByPath(path) {const parts = path.split('/').filter(p => p !== '');let current = fs['/'];for (let part of parts) {if (current && current.type === 'dir' && current.children[part]) {current = current.children[part];} else {return null;}}return current;
}function getDirStats(node) {let totalSize = 0;let latestTime = new Date(node.mtime);function traverse(current) {if (current.type === 'file') {totalSize += current.size;const mtime = new Date(current.mtime);if (mtime > latestTime) latestTime = mtime;} else if (current.type === 'dir') {for (let child of Object.values(current.children)) {traverse(child);}}}traverse(node);return {size: totalSize,mtime: latestTime.toISOString()};
}module.exports = {fs,readdir,chdir,resolvePath,normalizePath,updateFilesystem,getNodeByPath,getDirStats
};

tool/shell.js

// shell.js
const vfs = require('./vfs');
const fs = vfs.fs;const commands = {cat: require('./commands/cat'),cd: require('./commands/cd'),clear: require('./commands/clear'),cp: require('./commands/cp'),echo: require('./commands/echo'),exit: require('./commands/exit'),find: require('./commands/find'),grep: require('./commands/grep'),head: require('./commands/head'),help: require('./commands/help'),history: require('./commands/history'),ll: require('./commands/ll'),ls: require('./commands/ls'),mkdir: require('./commands/mkdir'),mv: require('./commands/mv'),pwd: require('./commands/pwd'),rm: require('./commands/rm'),rmdir: require('./commands/rmdir'),stat: require('./commands/stat'),tail: require('./commands/tail'),touch: require('./commands/touch'),vim: require('./commands/vim'),yum: require('./commands/yum'),zip: require('./commands/zip'),unzip: require('./commands/unzip'),rz: require('./commands/rz'),sz: require('./commands/sz'),
};let currentDir = '/home/user';function processCommand(input, rl, promptCall) {const args = input.trim().split(/\s+/);const cmd = args[0];if (!commands[cmd]) {console.log(`命令未找到: ${cmd}`);promptCall();return;} else if (cmd === 'history') {commands[cmd].execute([], currentDir, rl);promptCall();return;} else if (cmd === 'vim') {commands[cmd].execute(args, currentDir, rl);return;} else if (cmd === 'yum') {commands[cmd].execute(args, currentDir, rl);return;} else if (cmd === 'exit') {commands[cmd].execute(rl);return;}commands[cmd].execute(args, currentDir, (newDir) => {if (newDir) currentDir = newDir;});promptCall();
}module.exports = { processCommand };

演示截图

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HAProxy 实验指南:从零开始搭建高可用负载均衡系统

引言HAProxy&#xff08;High Availability Proxy&#xff09;是一款高性能的TCP/HTTP负载均衡器和代理服务器&#xff0c;广泛用于构建高可用、可扩展的Web架构。它由法国开发者Willy Tarreau于2000年开发&#xff0c;如今已成为开源社区和企业级应用中不可或缺的工具。HAProx…

2.10DOM和BOM插入/移除/克隆

1.DOM创建/插入/移除/克隆1.1创建元素前面我们使用过 document.write 方法写入一个元素&#xff1a;这种方式写起来非常便捷&#xff0c;但是对于复杂的内容、元素关系拼接并不方便&#xff1b;它是在早期没有 DOM 的时候使用的方案&#xff0c;目前依然被保留了下来&#xff1…

华为仓颉编程语言的表达式及其特点

华为仓颉编程语言的表达式及其特点 仓颉&#xff08;Cangjie&#xff09;语言的表达式有一个明显的特点&#xff0c;范围不再局限于传统算术运算&#xff0c;而是扩展到条件表达式、循环表达式等多种类型&#xff0c;每种表达式均有确定的类型和值。 传统基本表达式&#xff0…

【linux】keepalived

一.高可用集群1.1 集群类型LB&#xff1a;Load Balance 负载均衡 LVS/HAProxy/nginx&#xff08;http/upstream, stream/upstream&#xff09; HA&#xff1a;High Availability 高可用集群 数据库、Redis SPoF: Single Point of Failure&#xff0c;解决单点故障 HPC&#xff…

Webpack配置原理

一、Loader&#xff1a; 1、定义&#xff1a;将不同类型的文件转换为 webpack 可识别的模块2、分类&#xff1a; ① pre&#xff1a; 前置 loader &#xff08;1&#xff09;配置&#xff1a;在 webpack 配置文件中通过enforce进行指定 loader的优先级配置&#xff08;2&#x…

对比JS“上下文”与“作用域”

下面从定义、特性、示例&#xff0c;以及在代码分析中何时侧重“上下文”&#xff08;Execution Context/this&#xff09;和何时侧重“作用域”&#xff08;Scope/变量查找&#xff09;&#xff0c;以及二者结合的场景来做对比和指导。一、概念对比 | 维度 | 上下文&#xff0…

如何做数据增强?

目录 1、为什么要做数据增强&#xff1f; 2、图像数据增强&#xff1f; 3、文本与音频数据增强&#xff1f; 4、高级数据增强&#xff1f; 数据增强技术就像是一种“造数据”的魔法&#xff0c;通过对原始数据进行各种变换&#xff0c;生成新的样本&#xff0c;从而提高模型…

Go by Example

网页地址Go by Example 中文版 Github仓库地址mmcgrana/gobyexample&#xff1a;按示例进行 HelloWorld package mainimport ("fmt" )func main() {fmt.Println("Hello World") } Hello World 值 package mainimport ("fmt" )func main() {…

ClickHouse高性能实时分析数据库-消费实时数据流(消费kafka)

告别等待&#xff0c;秒级响应&#xff01;这不只是教程&#xff0c;这是你驾驭PB级数据的超能力&#xff01;我的ClickHouse视频课&#xff0c;凝练十年实战精华&#xff0c;从入门到精通&#xff0c;从单机到集群。点开它&#xff0c;让数据处理速度快到飞起&#xff0c;让你…

电子电气架构 --- 车载软件与样件产品交付的方法

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

C++:STL中vector的使用和模拟实现

在上一篇中讲到了string类&#xff0c;string并不属于STL中因为string出现的比STL早&#xff0c;但是在使用方法上两者有相似之处&#xff0c;学习完string后再来看vector会容易的多&#xff0c;接着往下阅读&#xff0c;一定会有收获滴&#xff01; 目录 vector的介绍 vect…

仓库管理的流程、绩效和解决方案?

什么是仓库管理&#xff1f; 仓库管理涉及对所有仓库运营的日常监督。一个全面、集成的仓库管理解决方案采用行业最佳实践&#xff0c;并涵盖使高效运营得以实现的所有基本要素。这些要素包括分销和库存管理、仓库劳动力管理以及业务支持服务。此外&#xff0c;由内部提供或与服…

TIM 实现定时中断【STM32L4】【实操】

使用定时器实现定时中断的功能&#xff1a;比如每1ms进入中断处理函数使用STM32CubeMX配置TIM初始化先了解每个参数的含义&#xff0c;在进行配置Counter Settings: 计数器基本设置Prescaler(PSC): 预分频器&#xff0c;设置预分频器系数Counter Mode: 技术模式&#xff0c;…

Elasticsearch 的聚合(Aggregations)操作详解

目录 1. 概述 2. 聚合类型分类详解 2.1 桶聚合&#xff08;Bucket Aggregations&#xff09; 2.1.1 基础桶聚合 2.1.2 特殊桶聚合 2.1.3 高级桶聚合 2.2 指标聚合&#xff08;Metric Aggregations&#xff09; 2.2.1 单值指标聚合&#xff08;Single-value Metrics&am…

电子电气架构 --- 高阶智能驾驶对E/E架构的新要求

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

0.深度学习环境配置步骤

0.深度学习环境配置步骤 这里介绍深度学习环境配置详细步骤&#xff0c;包括安装软件&#xff0c;每一步都有安装时的截图&#xff08;后续持续更新&#xff0c;敬请关注&#xff09; 目录如下&#xff1a; 1.安装anaconda 2.安装CUDA 3.安装CU_DNN 4.安装pytorch

在 Azure 中配置 SMS 与 OTP

1. Azure Active Directory B2C (AAD B2C) 中的 SMS/OTP 身份验证 1.1. 现状与原理&#xff1a;电话注册与登录 Azure Active Directory B2C (AAD B2C) 提供了将电话号码作为用户身份标识进行注册和登录的功能&#xff0c;旨在为用户提供一种便捷的替代传统电子邮件或用户名登录…

简单实现支付密码的页面及输入效果

干我们这行&#xff0c;风吹日晒不到&#xff0c;就怕甲方突发奇想。 今天客户要做一个安全密码前置校验&#xff0c;还要做成支付宝那种效果。ps:android端 心理吐槽了一万遍以后&#xff0c;还是得面对现实。 先用通义问一遍&#xff0c;给了两个方案&#xff0c;要么自己写&…

proxmox 解决docker容器MongoDB创建报错MongoDB 5.0+ requires a CPU with AVX support

目录 最简单直接的方式 测试MongoDB docker compose的安装shell脚本 验证访问 最简单直接的方式 让虚拟机直接使用宿主机的物理 CPU 功能标志。 打开 Proxmox Web UI。 选择你的 VM → 硬件 (Hardware) → CPU → 点击 编辑 (Edit)。 将 CPU 类型改为 host。 确认并重启…

向前滚动累加SQL 实现思路

一、业务背景在经营分析场景里&#xff0c;我们经常需要回答&#xff1a;“截至今天&#xff0c;过去 N 天/月/周累计发生了多少&#xff1f;”“把维度切到省、市、房型、项目经理、代理商等&#xff0c;结果又是什么&#xff1f;”本文用两个真实需求做演示&#xff1a;以天为…