WebLinux 是我花了几个月做的一个开源项目 — 一个完全跑在浏览器里的 Linux 终端模拟器。这篇文章聊聊它是怎么实现的。
核心问题
浏览器里没有真正的操作系统。要让用户觉得在一个真终端里敲命令,需要模拟三样东西:
- Shell 解释器 — 解析用户输入的命令,决定执行什么
- 文件系统 — 一个内存里的目录树,支持 cd、ls、cat、mkdir 等操作
- 终端 UI — 命令行输入框 + 输出区域,处理光标、历史、ANSI 颜色
Shell 解释器
最简单的方式是一个命令字典:每个命令名对应一个函数。
const commands = {
ls(args, ctx) {
return ctx.fs.list(ctx.cwd).join(' ');
},
cd(args, ctx) {
const target = args[0] || '/';
if (ctx.fs.exists(target)) {
ctx.cwd = ctx.fs.resolve(target);
} else {
return `cd: no such file or directory: ${target}`;
}
},
echo(args, ctx) {
return args.join(' ');
},
// ... 还有 cat, pwd, mkdir, rm, touch, whoami
};
用户每次回车,就把输入按空格拆开,第一个词查字典,剩下的当参数传进去。就这么简单。
文件系统
文件系统是一个嵌套对象:
const root = {
type: 'dir',
children: {
home: {
type: 'dir',
children: {
user: {
type: 'dir',
children: {
'readme.txt': { type: 'file', content: 'Hello from WebLinux!' }
}
}
}
},
etc: { type: 'dir', children: {} },
bin: { type: 'dir', children: {} }
}
};
路径解析就是沿着 / 分割的字符串往下走。不复杂,但足够真实。
最有趣的坑
- Tab 补全 — 要根据当前输入的前缀匹配文件系统路径,还要处理部分输入的情况
- 历史记录 — 上下键翻历史,要记住每个会话的输入序列
- 异步命令 —
sleep 2这种命令不能阻塞 UI,要用 Promise + setTimeout
下一步
WebLinux 还在持续迭代。计划加上更多命令、管道支持(ls | grep txt)、以及一个能写简单脚本的环境。
项目地址:web-linux.pages.dev