- 开发工具
- CLI
【免费下载链接】cash
Cross-platform Linux commands in ES6
导读
本文围绕 Cash 项目中独立的cash-sort子包展开,系统讲解它如何以纯 ES6 在 Linux、Windows、macOS 上还原 Unixsort命令的核心能力。读完本文,你将掌握cash-sort的全局安装方式、全部命令行选项的语义与用法(数字排序、人类可读数字排序、月份排序、随机排序等)、管道与标准输入的处理机制,以及排序比较器的源码级实现原理和对应测试用例。
一、cash-sort 是什么:Cash 生态中的排序命令
cash-sort是 Cash 项目(仓库根目录 README.md 中描述为 "Cross-platform Linux commands in pure ES6")按命令拆分的独立 npm 子包。官方定位是:
This is a cross-platform, 100% ES6 implementation of the Unix
sortcommand.
即:用纯 ES6 实现、不依赖原生编译的 Unixsort跨平台实现。它的package.json(packages/sort/package.json)中description为 "Cross-platform implementation of the Unix 'sort' command.",bin字段将sort绑定到./bin/sort.js,main指向./dist/commands/sort.js。
作为 Cash 项目的一部分,cash-sort与仓库中的cash-cat、cash-cp、cash-ls等同类子包遵循同一套构建流程:gulpfile.js中的packages任务会根据 commands.json 的配置,把dist目录下的命令实现、帮助文件、依赖工具和bin/parser.js一并打包进packages/sort/目录。需要说明的是,commands.json 的globalExclusions中包含sort,这意味着通过cash-global批量全局安装所有命令时,sort会被排除在外——这正是它拥有独立 npm 包、需要单独安装的原因之一。
二、安装与帮助:一条命令搞定全局可用
按照 packages/sort/README.md 的说明,全局安装cash-sort只需一条命令:
npm install cash-sort -g安装完成后,sort会被注册到系统的全局 PATH 中,之后可以在任意目录直接调用。查看命令帮助:
> sort --help--help输出内容由 src/help/sort.js 定义,完整帮助信息如下:
Usage: sort [OPTION]... [FILE]... Write sorted concatenation of all FILE(s) to standard output. Ordering options: -M, --month-sort compare (unknown) < 'JAN' < ... < 'DEC' -h, --human-numeric-sort compare human readable numbers (e.g., 2K 1G) -n, --numeric-sort compare according to string numerical value -R, --random-sort sort by random hash of keys -r, --reverse reverse the result of comparisons Other options: -c, --check, --check=diagnose-first check for sorted input; do not sort -o, --output=FILE write result to FILE instead of standard output --help display this help and exit With no FILE, or when FILE is -, read standard input.除了全局安装为独立命令外,sort还能在 Cash 交互式 shell(运行cash后)中使用,也能以编程方式调用:require('cash')后可通过cash.sort(args, options)直接执行,这由 src/index.js 中基于vorpal.api的导出机制提供,与 test/sort.js 中cash.sort(dir, opts)的调用方式一致。
三、命令定义与选项总览
命令本身由 Vorpal 框架注册,定义位于 src/commands/sort.js:
vorpal .command('sort [files...]') .parse(preparser) .option('-M, --month-sort', 'compare (unknown) < \'JAN\' < ... < \'DEC\'') .option('-h, --human-numeric-sort', 'compare human readable numbers (e.g., 2K 1G)') .option('-n, --numeric-sort', 'compare according to string numerical value') .option('-R, --random-sort', 'sort by random hash of keys') .option('-r, --reverse', 'reverse the result of comparisons') .option('-c, --check', 'check for sorted input; do not sort') .option('-o, --output [file]', 'write result to file instead of standard output') .autocomplete(fsAutocomplete())其中.parse(preparser)表示参数会先经过 src/preparser.js 预处理——把输入中的$VAR或${VAR}环境变量引用替换为process.env中的实际值(不存在时替换为空字符串),这一点在管道场景下尤为实用。
各选项的语义可汇总如下:
| 短选项 | 长选项 | 参数 | 作用 |
|---|---|---|---|
-M | --month-sort | 无 | 按月名排序,未知 < JAN < ... < DEC |
-h | --human-numeric-sort | 无 | 按人类可读数字排序(如 2K、1G) |
-n | --numeric-sort | 无 | 按字符串的数值比较 |
-R | --random-sort | 无 | 按随机哈希打乱排序结果 |
-r | --reverse | 无 | 反转比较结果 |
-c | --check | 无 | 仅检查输入是否已排序,不排序 |
-o | --output | FILE | 将结果写入文件而非标准输出 |
注意-o的参数是可选的([file]):源码中专门处理了"传了-o却没给文件名"的情况,此时会输出sort: option '--output' requires an argument并提示Try 'sort --help' for more information.,对应测试见 test/sort.js 的sort -o用例。
四、五种排序模式与比较器实现
排序的核心是 src/commands/sort.js 中传给Array.prototype.sort的比较函数。它对每一行分别提取aAlpha(去 ANSI 色码后保留字母数字字符)、aNumeric(仅保留数字字符),再按选项分派到不同模式。
4.1 默认字典序排序
无任何排序选项时,使用aAlpha.localeCompare(bAlpha)做字符串比较。这里先用 src/util/stripAnsi.js 去掉 ANSI 转义序列(这正是 Cash 生态的特色:输入可能来自其他命令的彩色输出),再剔除\W+非单词字符后比较,因此大小写混合、带符号的文本也能稳定排序。
来自 test/sort.js 的验证:对内容为zaz\nsss\nqqq\n-zbz\nBBB\nddd\nCCC\n2bbb\nccc\n的default.sort文件排序,期望输出为2bbb\nBBB\nccc\nCCC\nddd\nqqq\nsss\nzaz\n-zbz\n。
4.2 -n 数字排序
options.numericsort分支将两行的数字提取值相减:aNumeric - bNumeric,若数值相等则回退到aAlpha.localeCompare(bAlpha)保证稳定可比。注意提取方式是replace(/\D/g, ''),即取字符串中全部数字字符拼接后的数值,这解释了测试用例numeric.sort中22 zaz、77 ccc等行的排序结果:数字部分被提取后按数值大小排列,纯文本行(如CCC、-zbz)则按字典序排在数字行之前。
4.3 -h 人类可读数字排序
--human-numeric-sort对应 Unix coreutils 中sort -h的能力,用于处理2K、1G、2.5P这类带容量后缀的数字。源码中为此定义了排序组表(注释明确说明对齐 GNU coreutilssort.c的层级顺序):
const humanOrdering = {'': 0, 'K': 1, 'k': 1, 'M': 2, 'G': 3, 'T': 4, 'P': 5, 'E': 6, 'Z': 7}; const humanReadableSort = new RegExp(/^([0-9]+)?[\.]?[0-9]+[K|k|M|G|T|P|E|Z|Y]/g);parseHumanReadableNumbers(nbr)的解析逻辑分两步:
- 若匹配
2K、1G、2.5P这类后缀数字格式,则拆出num(数值部分)和group(后缀对应组别); - 否则若以数字开头则提取
parseFloat后的数值,非数字则返回-99999999999作为极小值垫底。
比较时先比group(即后缀的大小层级:K < M < G < T < P < E < Z),组别相同再比num,最终回退到字典序。测试用例human.sort的期望输出1k、8k、1M、5M、4G、3T、2.5P、2E、1Z完整覆盖了这一层级链。
4.4 -M 月份排序
--month-sort按月名排序。源码内置了月份序列表:
const monthOrdering = { jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6, jul: 7, aug: 8, sep: 9, oct: 10, nov: 11, dec: 12 }; const months = new RegExp(/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i);parseMonths(str)用不区分大小写的正则匹配行首的前三个字母,映射到 1~12 的月份序号;不匹配的返回 0(即"未知")。比较时若两个月序号相同(例如March与Marc都解析为 3),则回退到aAlpha.localeCompare(bAlpha)字典序。测试month.sort验证了4mar、56sevenjan、goats等非标准前缀行排在未知组,Marc/March/apr/APR等按月份序号排列的行为。
4.5 -R 随机排序
--random-sort在完成上述任一种排序后,使用依赖array-shuffle(Fisher-Yates 洗牌算法)对结果进行随机打乱:
if (options.randomsort) { parts = shuffle(parts); }源码注释也坦言 "sorting arrays has woes"(数组随机排序的固有问题),并说明使用 Bostock 风格的 Fisher-Yates 实现。测试用例通过连续执行 100 次、断言结果不与上一次相同来验证随机性(每次 20 个元素的随机排列有 20! 种可能)。
4.6 -r 反转
--reverse在排序(或随机打乱)之后对parts整体执行parts.reverse(),即把结果数组反转而非修改比较器方向,因此它可以与上述任一排序模式叠加。测试用例同时验证了纯字典序反转、多文件合并反转以及-h与-r组合反转三种场景。
五、-c 检查与 -o 输出
5.1 -c 检查输入是否已排序
options.check分支不执行排序,而是逐行扫描判断输入是否乱序。检测规则与排序模式保持一致:当相邻两行都是数字时按parseFloat数值比较,否则按字符串a > b比较;一旦发现乱序,立即输出类似sort: -:6: disorder: e的诊断信息(-代表标准输入,6是乱序行的行号,e是乱序的那一行内容),并停止后续检查。对应测试:disorder.sort返回sort: -:6: disorder: e,disorder-numeric.sort返回sort: -:6: disorder: 26。源码注释指出,当前实现因多文件合并后丢失来源文件信息,暂不输出具体的文件名。
5.2 -o 写结果到文件
options.output分支将排序结果通过fs.writeFileSync写入指定文件而非标准输出。行为细节如下:
- 目录不存在时返回
sort: open failed: <file>: No such file or directory,退出码 2; - 其余写失败场景输出对应的错误码;
- 正常写入时函数返回 0、不向标准输出打印任何内容。
测试用例验证了-o写出后可被cash.cat正确读回。
六、输入来源:文件、标准输入与管道缓冲
6.1 文件读取与错误处理
文件内容由 src/util/fetch.js 统一读取:它先调用 src/util/expand.js 对通配符(glob 模式)做展开,再逐个fs.readFileSync读取 UTF-8 文本;若目标是目录则返回sort: read failed: <name>: Is a directory,文件不存在则返回sort: cannot read: <name>: No such file or directory。多个文件会被拼接后统一排序,实现 "sorted concatenation of all FILE(s)" 的语义(见帮助文本),test/sort.js 中两次传入同一文件得到每行重复两份的期望输出即为验证。
6.2 标准输入与管道缓冲机制
不传文件参数时,sort读取标准输入(帮助文本说明 "With no FILE, or when FILE is -, read standard input")。由于sort必须等所有输入到齐才能排序,而管道传递 stdin 时数据可能被切分为多次回调,源码实现了一个 100ms 的缓冲机制:
if (args && args.stdin && options.bypass !== true) { sort.counter++; sort.stdin += `${args.stdin}\n`; if (sort.counter === 1) { setTimeout(function () { sort.counter = 0; args.stdin = sort.stdin; sort.stdin = ''; options.bypass = true; sort.exec.call(self, args, options); }, 100); } return; }即:第一次收到 stdin 片段时启动 100ms 定时器,期间累积所有片段,到期后合并为完整输入再真正执行排序。这正是 Cash 中ipconfig | grep IPv4 | sort这类管道场景(见根 README.md 示例)能够正确工作的底层保障。
七、执行链路:interfacer 与三种调用形态
所有 Cash 命令都通过 src/util/interfacer.js 统一绑定执行:它把命令对象与调用方(Vorpal 交互式命令、cash.sort编程式调用)解耦,内部构造一个收集 stdout 的 logger,并把命令的返回值统一规整为回调结果。sort.exec因此只需聚焦"读入—排序—输出",无需关心自己是被 shell 还是被 Node API 调用。
bin/parser.js(由构建流程复制到每个子包的bin/目录)进一步决定了独立命令的执行路径:
- 无参数时直接加载命令 JS 执行;
- 带参数但无管道时,仅启动一个 Vorpal 实例加载
sort单命令; - 出现
|管道时则加载整个 Cash 库,让sort能与其他命令协作。
八、总结:何时选用 cash-sort
cash-sort适合以下场景:需要在 Windows 上获得与 Unix 一致的sort体验且不愿安装 Cygwin;想在 Node/JavaScript 环境中以编程方式调用排序(cash.sort());或希望只安装单个命令、保持系统 PATH 干净(而非通过cash-global一次性安装全部命令)。它的所有选项与行为均有对应测试用例(test/sort.js)和可阅读的源码(src/commands/sort.js、src/help/sort.js)支撑,属于 Cash 项目中文档、实现、测试三件套最完整的命令之一。使用前请确认 Node 版本满足engines要求(node >= 0.11.16/iojs >= 1.0.0,见 packages/sort/package.json),并注意其对 GNU coreutilssort选项的覆盖范围以 src/help/sort.js 列出的七项为准。
- 开发工具
- CLI
【免费下载链接】cash
Cross-platform Linux commands in ES6
相关推荐
cash-cat 实战指南:用纯 ES6 跨平台复刻 Unix cat 命令
cash cat 实战指南:用纯 ES6 跨平台复刻 Unix cat 命令 cash cat 是 Cash 项目中以 100% ES6 重新实现的 Unix
开发工具CLISerenity OS sort 命令全解析:从手册到源码的行排序工具实现
Serenity OS sort 命令全解析:从手册到源码的行排序工具实现 本篇指南以 Serenity Operating System 的 sort 命令手
操作系统内核驱动Angular Material Sort 组件 API 全解析:matSort 指令与 mat-sort-header 表头排序实战指南
Angular Material Sort 组件 API 全解析:matSort 指令与 mat sort header 表头排序实战指南 导读 @angula
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考