- 开发工具
- CLI
【免费下载链接】cash
Cross-platform Linux commands in ES6
导读
cash-ls是 Cash 项目(Cross-platform Linux commands in ES6)中负责目录与文件列表的独立 npm 包,它用 100% 纯 ES6 JavaScript 重新实现了 Unix 经典命令ls,无需原生编译、无需 Cygwin 即可在 Windows、Linux 与 macOS 上获得一致的 Linux 风格体验。本文以 packages/ls/README.md 为骨架,结合仓库内 src/commands/ls.js 的完整实现与 test/ls.js 的测试断言,系统讲解cash-ls的安装方式、全部命令行选项、源码级排序/着色/排版原理、退出码约定及其在 Cash 生态中的三种使用形态,读完后你可以独立安装使用它,并理解其内部实现机制。
一、cash-ls 是什么
根据 packages/ls/README.md,cash-ls是 Unixls命令的跨平台、100% ES6 实现。它隶属于 Cash 项目——一个用纯 JavaScript 实现主流 Unix 命令的开源项目,目标是让 JavaScript 开发者在不依赖原生编译的情况下,在 Windows 上获得类 Linux 的终端体验,并为跨平台 bash 风格的脚本执行打开大门。
cash-ls的独立包信息记录在 packages/ls/package.json:
- 包名:
cash-ls,版本0.2.0; - 入口:
./dist/commands/ls.js; - 全局命令映射:
"bin": { "ls": "./bin/ls.js" }——安装后可直接以ls命令名调用; - 运行时要求:
node >= 0.11.16或iojs >= 1.0.0; - 核心依赖:
vorpal(命令框架)、chalk(终端着色)、filesize(人类可读文件大小)、glob(通配符展开)、lodash、vorpal-autocomplete-fs(文件路径补全)。
二、安装与全局使用
2.1 独立安装
与单测命令需要整套工具链不同,cash-ls允许只安装这一个命令:
npm install cash-ls -g全局安装后,ls会被注册到系统 PATH 中(对应 packages/ls/package.json 中bin字段的ls映射),之后在任何终端中都可以直接敲ls使用。
2.2 查看内置帮助
安装完成后,输入:
> ls --help会打印由 src/help/ls.js 提供的完整帮助文本,包含用法、全部选项与退出码说明。其开头的用法行是:
Usage: ls [OPTION]... [FILE]... List information about the FILEs (the current directory by default). Sort entries alphabetically if none of -tSU nor --sort is specified.注意末尾的排序声明:当未指定-t、-S、-U等排序选项时,默认按字母序排序,这一点与 GNU ls 保持一致。
2.3 三种使用形态
Cash 生态中ls有三种使用方式,源码支撑位于 src/index.js:
- Cash 交互式 Shell 内:
npm install cash -g后输入cash进入$提示符,直接执行ls -lah; - 全局命令:通过
cash-global一次性将所有命令安装到系统 PATH(在 commands.json 中ls不在globalExclusions列表内,因此会被全局暴露); - 程序化调用:
require('cash')后调用$.ls('.', {l: true}),或使用模板字符串形式执行整段命令,例如:
const $ = require('cash'); const out = $.ls('.', {l: true}); // 返回长格式列表文本 // 或者用字符串/模板字面量 require('cash') ` ls -la `;程序化调用路径中,src/util/interfacer.js 负责统一命令函数与调用方(Vorpal 或cash.xxxAPI)之间的接口:它把命令的this.log输出累积为 stdout 字符串,并通过回调返回,同时支持silent模式与异步命令。
三、命令行选项总览
下表完整继承自 src/help/ls.js 的帮助文本,并标注了 Vorpal 注册定义(见 src/commands/ls.js 末尾的.option(...)链):
| 选项 | 长选项 | 作用 | Vorpal 注册 |
|---|---|---|---|
-a | --all | 不忽略以.开头的条目(列出隐藏文件及.、..) | ✅ |
-A | --almost-all | 不列出隐含的.和..,但列出其他隐藏文件 | ✅ |
-d | --directory | 只列出目录本身而非其内容,且不解引用符号链接 | ✅ |
-f | — | 不排序,启用-aU,禁用-ls --color | ❌(帮助文本保留) |
-F | --classify | 给条目追加类型指示符(*/=>@\|之一,实际实现为目录追加/) | ✅ |
-h | --human-readable | 配合-l以人类可读格式输出大小(如 1K 234M 2G) | ✅ |
-i | --inode | 打印每个文件的索引号(inode) | ✅ |
-l | — | 使用长列表格式 | ✅ |
-q | --hide-control-chars | 用?代替非图形字符 | ❌(帮助文本保留) |
-Q | --quote-name | 用双引号括住条目名 | ✅ |
-r | --reverse | 反向排序 | ✅ |
-R | --recursive | 递归列出子目录 | ✅ |
-S | — | 按文件大小排序 | ✅ |
-t | — | 按修改时间排序,最新在前 | ✅ |
-U | — | 不排序,按目录顺序列出条目 | ✅ |
-w | --width=COLS | 假定屏幕宽度为 COLS 而非当前值 | ✅(-w, --width [COLS]) |
-x | — | 按行列出条目而非按列 | ✅ |
-1 | — | 每行只列一个文件 | ✅ |
实现说明:帮助文本中的
-f与-q沿用了 GNU ls 的文档条目,但当前 src/commands/ls.js 的 Vorpal 命令注册链中并未定义这两个选项,属于文档层面的预留项;--help由 Vorpal 框架统一提供。
帮助文本同时给出了退出码约定(与实现 src/commands/ls.js 中error()的返回逻辑一致):
Exit status: 0 if OK, 1 if minor problems (e.g., cannot access subdirectory), 2 if serious trouble (e.g., cannot access command-line argument).四、核心选项的源码级行为解析
4.1 排序:默认字母序,-S大小、-t时间、-U不排序、-r反向
排序逻辑集中在 src/commands/ls.js 的execLsOnFiles中。默认(未指定-U)时会对条目调用sort:
- 默认字母序:取文件名(经 src/util/fileFromPath.js 剥离路径得到短名),
.trim().toLowerCase().replace(/\W/g, '')后比较,即不区分大小写、忽略非单词字符的字母序; -S按大小:a.data.size大者在前。实现里还包含一个 Windows 兼容 hack——当平台为win32且目录大小为 0 时,将其视为 4096,以避免因 Linux/Windows 对目录 size 统计不一致导致排序结果漂移;-t按修改时间:比较mtime,最新在前;-U不排序:保持目录读取的原始顺序(在 Windows 上目录顺序本就与 Linux 不同,测试中对该项做了平台判断,见 test/ls.js 的-U用例);-r反向:在完成上述任一排序后,对数组执行reverse()。
此外,在进入execLsOnFiles之前,preSortPaths会把用户传入的多个路径先分为dirs与files两组并各自排序,文件始终列在目录之前(测试断言ls(['./sub', 'b.tgz', '.', 'a.txt'])输出为文件在前、目录按.:、./sub:分段展示)。
4.2 隐藏文件:-a、-A与-d的过滤逻辑
execLsOnFiles中的include过滤函数精确实现了 GNU ls 语义:
- 普通文件(
!dotted)→ 默认显示; - 点开头文件(
.hidden等)→ 仅当-a/--all或-A/--almost-all时显示; - 隐含条目
.与..→ 仅-a/--all时显示(-A会排除它们); -d/--directory→ 只包含.一个条目,且文件名替换为传入的完整路径(fileName = (options.directory && file === '.') ? path : fileName)。
4.3 文件着色:目录、可执行文件与扩展名
src/commands/ls.js 的着色顺序是:
- 普通文件先交给 src/util/colorFile.js 按扩展名着色:音频类(mp3、wav、ogg 等)→ 青色,压缩包/归档类(zip、tar、gz、7z 等)→ 红色,图片/视频类(jpg、gif、png、mp4 等)→ 品红;
- 若文件未被着色且权限串(见下)含
x(可执行),则用chalk.green染成绿色; - 目录统一用
chalk.cyan染成青色。
所有 ANSI 转义在计算对齐宽度时都会被 src/util/stripAnsi.js 剥除,避免着色码干扰列宽计算。
4.4 权限、时间、大小的格式化工具
- 权限:src/util/converter.permissions.js 把
fs.statSync的mode与0o777做与运算后转八进制,再经listing映射表(0→---… 7→rwx)拼成-rwxr-xr-x形式的 RWX 串; - 时间:src/util/converter.date.js 将
mtime格式化为 Unix 风格Mon DD HH:MM(如Oct 14 00:22,日个位前补空格); - 大小:
-h/--human-readable时调用filesize库并以{unix: true}模式输出1K 234M 2G形式的单位。
4.5 显示修饰:-F分类、-Q引号、-iinode、-1单行
-F/--classify:目录名后追加/;-Q/--quote-name:条目名用双引号包裹;-i/--inode:在长列表的细节列最前面插入data.ino索引号;-1:强制每行一个条目(files.join('\n'))。
五、输出格式:长列表与列排版
5.1-l长列表
execLsOnFiles为每个条目组装细节数组[type+permissions, hardLinks, owner, group, size, modified, fileName],其中type为目录'd'或文件'-'。-l开启时逐行输出,并在formatAll中打印total <size>汇总行(totalSize为所有条目 size 之和)。
5.2 多目录输出的分段规则
formatAll的showName逻辑(见 src/commands/ls.js):
- 当传入多个路径(或多个目录)时,每个目录输出前冠以
路径:标题行,目录间以空行分隔; - 仅单个目录时,不打印标题行,只在
-l时打印total行。
5.3 列排版与宽度控制
默认(非-l、非-1)的输出交由 src/util/columnify.js 处理,其算法:
- 取最长条目长度 + 2 作为列宽基准,屏幕宽度取自
process.stdout.columns; - 若总宽度(全部条目拼接 + 每项 2 个空格)不超过屏幕宽度,则直接以双空格连接成一行;
- 否则计算列数
cols = floor(width / longest)(至少为 1),逐列填充并用 src/util/pad.js 补齐; - 当列数退化为 1 时不做填充,避免超长文件名被空格撑爆。
-w/--width允许显式指定列宽基准(如ls -w 20),-x则绕过列排版、按行依次输出每个条目,-1同样按行但每条独占一行。测试中{width: 5}与-1的断言均为每个文件各占一行(见 test/ls.js 的file display分组)。
六、命令执行链路:从参数到 stdout
结合 src/commands/ls.js 与 src/index.js,一次ls调用的完整链路为:
- 参数解析:Vorpal 按
ls [paths...]收集路径,.parse(preparser)先经 src/preparser.js 展开$VAR/${VAR}环境变量; - 路径展开:
expand(paths)(src/util/expand.js)用glob.sync将*.gif之类的通配符展开为真实路径,无匹配时保留原样(测试断言ls('*.gif')输出e.gif); - 路径归类:
preSortPaths用fs.statSync判断每个路径是目录还是文件,不存在时报ls: cannot access <path>: No such file or directory(对应退出码 1,ENOENT+syscall='scandir'分支),其余异常退出码 2 并输出堆栈; - 目录扫描:
execDir总是先压入.与..两个隐含条目,再经 src/util/walkDir.js 的fs.readdirSync+statSync遍历目录内容;-R/--recursive时改走 src/util/walkDirRecursive.js,对每个子目录递归调用execDir并合并结果; - 格式化:
execLsOnFiles完成排序、过滤、着色与细节组装,formatDetails决定输出长列表、单行列表或列排版; - 输出:统一
stdout.replace(/\\/g, '/')把 Windows 反斜杠路径规整为正斜杠后经this.log输出,成功返回退出码 0。
七、测试验证:行为即规范
test/ls.js 通过构造固定样例目录(a.txt、b.tgz、c.exe、d.json、e.gif、f.jpg、g、.hidden及子目录sub,见 test/util/util.js)对cash.ls的 API 形态做了精确断言,例如:
- 默认列当前目录:
'a.txt b.tgz c.exe d.json e.gif f.jpg g sub\n'; -a输出含.、..与.hidden;-A输出.hidden但不含隐含条目;-F给sub追加/;-Q全部条目加双引号;- 多个路径混排:文件在前,目录以
./sub:标题分段; - 错误场景:单个不存在文件返回一行
ls: cannot access aaa: No such file or directory,两个不存在文件返回两行,且不存在的文件不影响其余文件的正常输出; -l输出必须包含total、-rw权限前缀、硬链接数1、e.gif与sub等字段;-h时大小出现3.6K形式;-R递归输出../atatatest/foo:\nbar\n\n../atatatest/foo/bar:\ncows.file结构。
这些用例直接对应 packages/ls/README.md 所声称的“对 Unixls的近似实现”,也是验证源码行为最可靠的依据。
八、包的组织与构建方式
packages/ls/README.md本身由 gulpfile.js 的packages任务基于 packages/template.README.md 模板生成:将{package-name}替换为cash-ls、{command-name}替换为ls,并批量写入各命令的说明与依赖清单。ls子包在 commands.json 中声明的独立依赖为lodash、chalk、filesize、vorpal-autocomplete-fs、glob,随包发布的文件包括dist/commands/ls.js以及colorFile、columnify、converter.date、converter.permissions、expand、fileFromPath、interfacer、pad/lpad、stripAnsi、walkDir/walkDirRecursive等工具模块,保证了子包脱离主仓库后仍可独立运行。
九、在 Cash 生态中的定位
Cash 主项目(根目录 README.md)以纯 ES6、零原生编译、超 200 个单元测试为目标实现 20+ 常用 Unix 命令。cash-ls与cash-cat、cash-cp、cash-rm、cash-grep等同级子包共同构成命令矩阵(仓库内各子包说明见 packages/cat/README.md、packages/cp/README.md 等)。
两种按需安装方式:
- 全量命令:
npm install cash-global -g,安装后直接ls -lah; - 按需单命令:
npm install cash-ls -g,只获得ls。
cash-ls之所以能“按需独立”,正是因为它构建在 Vorpal 命令框架之上(命令注册代码见 src/commands/ls.js 末尾的module.exports),同时通过 src/util/interfacer.js 与 Cash 的程序化 API 无缝对接,实现“命令行、交互式 Shell、Node 程序”三端复用同一套实现。
十、开源与许可证
cash-ls与整个 Cash 项目采用 MIT 许可证(见根目录 LICENSE 与 packages/ls/package.json 的license字段)。在 Windows 上使用本命令时,注意实现中对目录 size、路径分隔符、隐藏文件顺序等做了平台兼容处理,个别行为(如-S在 Windows 目录上的排序、-U的原始顺序)会与 Linux 存在细微差异,测试中也对此做了相应标注(见 test/ls.js 中的平台判断与it.skip用例)。
- 开发工具
- CLI
【免费下载链接】cash
Cross-platform Linux commands in ES6
相关推荐
Cash命令实现详解:从ls命令看ES6实现技巧
Cash命令实现详解:从ls命令看ES6实现技巧 本文深入分析了Cash项目中ls命令的完整实现,展示了ES6在现代命令行工具开发中的强大能力。通过模块化架构设
开发工具CLIcash-cat 实战指南:用纯 ES6 跨平台复刻 Unix cat 命令
cash cat 实战指南:用纯 ES6 跨平台复刻 Unix cat 命令 cash cat 是 Cash 项目中以 100% ES6 重新实现的 Unix
开发工具CLIcash-kill 深度指南:在 Node.js 与 Windows 中跨平台使用 Unix kill 命令
cash kill 深度指南:在 Node.js 与 Windows 中跨平台使用 Unix kill 命令 导读 cash kill 是 Cash 项目(Cr
开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考