☰
cash-ls 完全指南:纯 ES6 跨平台实现的 Unix ls 命令
2026/10/15 18:54:33 网站建设 项目流程
  • 开发工具
  • CLI

【免费下载链接】cash

Cross-platform Linux commands in ES6

项目地址:https://gitcode.com/gh_mirrors/ca/cash
点击查看免费下载

导读

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:

  1. Cash 交互式 Shell 内:npm install cash -g后输入cash进入$提示符,直接执行ls -lah;
  2. 全局命令:通过cash-global一次性将所有命令安装到系统 PATH(在 commands.json 中ls不在globalExclusions列表内,因此会被全局暴露);
  3. 程序化调用: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 的着色顺序是:

  1. 普通文件先交给 src/util/colorFile.js 按扩展名着色:音频类(mp3、wav、ogg 等)→ 青色,压缩包/归档类(zip、tar、gz、7z 等)→ 红色,图片/视频类(jpg、gif、png、mp4 等)→ 品红;
  2. 若文件未被着色且权限串(见下)含x(可执行),则用chalk.green染成绿色;
  3. 目录统一用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调用的完整链路为:

  1. 参数解析:Vorpal 按ls [paths...]收集路径,.parse(preparser)先经 src/preparser.js 展开$VAR/${VAR}环境变量;
  2. 路径展开:expand(paths)(src/util/expand.js)用glob.sync将*.gif之类的通配符展开为真实路径,无匹配时保留原样(测试断言ls('*.gif')输出e.gif);
  3. 路径归类:preSortPaths用fs.statSync判断每个路径是目录还是文件,不存在时报ls: cannot access <path>: No such file or directory(对应退出码 1,ENOENT+syscall='scandir'分支),其余异常退出码 2 并输出堆栈;
  4. 目录扫描:execDir总是先压入.与..两个隐含条目,再经 src/util/walkDir.js 的fs.readdirSync+statSync遍历目录内容;-R/--recursive时改走 src/util/walkDirRecursive.js,对每个子目录递归调用execDir并合并结果;
  5. 格式化:execLsOnFiles完成排序、过滤、着色与细节组装,formatDetails决定输出长列表、单行列表或列排版;
  6. 输出:统一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

项目地址:https://gitcode.com/gh_mirrors/ca/cash
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询