Nushell 如何用 from nuon 和 to nuon 读写带注释、尾随逗号与表格形式的 NUON 文件
【免费下载链接】nushellA new type of shell项目地址: https://gitcode.com/GitHub_Trending/nu/nushell
当数据文件由人手工编写或审阅时,JSON 往往显得啰嗦:每行都要重复键名,不能写注释,尾随逗号还会直接报错。Nushell 内置的 NUON 格式(Nushell Object Notation)解决了这些问题。from nuon把 NUON 文本反序列化为结构化数据,to nuon把 Nushell 值序列化为 NUON 文本,两个命令都属于 Formats 类别,随 Nushell 发布即可使用。本文按仓库中 crates/nuon/README.md、正式规范 和 命令实现 中的真实示例,走一遍“读入带注释、尾随逗号与表格形式的 NUON → 改造成想要的输出风格 → 验证往返一致”的完整路径。
前提:NUON 是 JSON 的超集,外加表格形式
按 crates/nuon/README.md 的定义,NUON 在 JSON 基础上增加了:
- 允许尾随逗号,列表中的逗号本身也是可选的(用空白分隔也可以);
- 键和不包含空格、特殊字符的字符串可以不带引号;
- 允许
#注释——但from nuon不会保留注释; - 数字支持十六进制、八进制、二进制,可带
_分隔符,可以以+或孤立的.开头; 2min、1kb、2000-01-01T00:00:00+00:00这类 duration、filesize、datetime 是字面量,0x[be ef]、$.a.b、1..5分别是 binary、cell-path 和 range;- 一组结构一致的 record 列表可以写成表格形式,列名只写一次,而不是每行重复。
同一段数据两种写法的对照(示例取自 README):
{ "name": "Some One", "birth": "1970-01-01", "stats": [ 2544729499973429198, 687051042647753531, 6702443901704799912 ] }{ name: "Some One", # the name of the person birth: "1970-01-01", # their date of birth stats: [ # some dummy "stats" about them 2544729499973429198, 687051042647753531, 6702443901704799912, # note the trailing comma here... ], # and here } # wait, are these comments in a JSON-like document?!?!NUON 文件就是 UTF-8 文本文件:规范要求输入必须是合法的 UTF-8,开头的 BOM 会被剥掉恰好一个。所有读写都是通过管道把文本交给这两个命令完成的。
用 from nuon 读入带注释与尾随逗号的文档
from nuon的签名是字符串输入、任意类型输出(from nuon 实现),命令文档给出的可直接执行示例:
'{ a:1 }' | from nuon # 结果:record { a: 1 } '{ a:1, b: [1, 2] }' | from nuon # 结果:record { a: 1, b: [1 2] } '{a:1,b:[1,2]}' | from nuon # 结果:record { a: 1, b: [1 2] }带注释和尾随逗号的文档能直接读入(示例输出取自正式规范,规范约定其中的nushell代码块是“带真实输出的命令”):
'[1, 2, 3,]' | from nuon | to json -r # => [1,2,3]注意#成为注释是有条件的:只在 token 边界处(输入开头、空白、或, : [ ] { } ;之后)才算注释符,紧贴在前一个词后面时就是普通字符。这两条对照来自规范,判断你的文档里#到底起了什么作用:
"[a#b]" | from nuon | to json -r # => ["a#b"] "[a, # note\n b]" | from nuon | to json -r # => ["a","b"]读入后注释被丢弃,这是 README 明确说明的行为:允许注释,但from_nuon不保留它们。
表格形式:列名只写一次
表格形式的语法是[ 表头; 行1 行2 ... ],表头和每一行都是列表,行单元格按位置对应表头列名。解码后它就是一组 record 的列表,与逐行写 record 没有区别:
'[[a b]; [1 2] [3 4]]' | from nuon | to json -r # => [{"a":1,"b":2},{"a":3,"b":4}]README 里用 3 行 4 列的例子展示了压缩效果:同样的数据在 JSON 里要重复每个键,NUON 表格形式把 20 行压成 6 行,四个列名只写了一次。
写表格文件时有几条硬规则,违规会直接报错而不是静默出错(以下均为规范中的文档示例):
# 表头必须是字符串 '[[a 1]; [1 2]]' | from nuon # => error # 行长度必须与表头一致 '[[a b]; [1]]' | from nuon # => error # 列名不能重复 '[[a b a]; [1 2 3]]' | from nuon # => error: column_defined_twice另外,“有表头但没有数据行”在表格形式下无法表达,空表只能写[]。
用 to nuon 写出 NUON,并控制输出风格
to nuon接受任意输入、输出字符串,默认是紧凑的单行输出。命令文档 中经过测试的示例:
[1 2 3] | to nuon # 结果:[1, 2, 3] [1 2 3] | to nuon --indent 2 # 结果: # [ # 1, # 2, # 3 # ] {date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --indent 2 # 结果: # { # date: 2000-01-01T00:00:00+00:00, # data: [ # 1, # [ # 2, # 3 # ], # 4.56 # ] # }注意 date 值被写成完整的 RFC 3339 形式:writer 对 datetime 总是输出完整形式(规范示例:{date: 2000-01-01} | to nuon # => {date: 2000-01-01T00:00:00+00:00})。
to nuon的全部开关及默认值(规范给出的表,除特别说明外默认全部关闭):
| 开关 | 短参数 | 默认 | 作用 |
|---|---|---|---|
--raw | -r | 关 | 去掉所有空白 |
--indent N | -i | 关 | 每项一行,每层缩进 N 个空格 |
--tabs N | -t | 关 | 每项一行,每层缩进 N 个制表符 |
--pretty | -p | 关 | 等价于--indent 2 |
--list-of-records | -l | 关 | 不用表格形式,逐行输出 record |
--no-commas | -c | 关 | 用空白代替逗号分隔 |
--raw-strings | -R | 关 | 把含引号或反斜杠的字符串写成r#'...'# |
--serialize | -s | 关 | 把无法反序列化回来的类型序列化为字符串 |
开关冲突时的优先级规则也是规范明确写死的,判断输出形态时按这个顺序:
--raw压过--indent、--tabs、--pretty:[[a b]; [1 2]] | to nuon --pretty --raw结果是[[a,b];[1,2]];--tabs压过--indent;--indent 0与--tabs 0等价于--raw;--raw不压过--list-of-records和--raw-strings,这两者可以与它组合:[[a b]; [1 2]] | to nuon --raw --list-of-records结果是[{a:1,b:2}];--raw与--no-commas互斥:前者删空白、后者用空白当分隔符,同时用会没有分隔符,[1 2 3]会被读回成整数123。
几个常用分支的输出(均为命令文档或规范中的示例):
[[a, b]; [1, 2], [3, 4]] | to nuon --list-of-records # => [{a: 1, b: 2}, {a: 3, b: 4}] [1 2 3] | to nuon --no-commas # => [1 2 3] {a: 1, b: 2} | to nuon --no-commas # => {a: 1 b: 2} 'helloworld'.into | to nuon --raw # 文档示例:--raw 覆盖其它缩进选项 [1 2 3] | to nuon --indent 2 --raw # => [1,2,3] 'hello "world"' | to nuon --raw-strings # => r#'hello "world"'# [[name, age]; [Alice, 30], [Bob, 25]] | to nuon --pretty # => [ # [name, age]; # [Alice, 30], # [Bob, 25] # ]--pretty会同时对齐表格列,--indent走的是同一条代码路径,所以--indent N也会对齐列。--list-of-records下即使带缩进,每个 record 仍会内联在一行里。
何时自动使用表格形式,何时不会
to nuon不是无条件把列表写成表格。规范给出的判定条件:列表非空、每个元素都是 record、record 至少有一个键、且所有 record 键相同且顺序相同,才会用表格形式;键顺序不同就会被判成普通列表:
[{a: 1, b: 2}, {b: 3, a: 4}] | to nuon # => [{a: 1, b: 2}, {b: 3, a: 4}] [{}, {}] | to nuon # => [{}, {}]想强制不用表格形式时加--list-of-records,它会无条件关闭表格语法。
验证:往返一致性与输出类型
最直接的验证是读写往返。规范值模型一节给出的例子说明 binary、cell-path、range 三类值能完整往返(即from nuon读得回to nuon写出的所有内容):
"[0x[be ef], $.a.b, 1..5]" | from nuon | to nuon # => [0x[BEEF], $.a.b, 1..5]也可以用metadata检查to nuon产物携带的内容类型(该断言来自 to nuon 命令测试):
{a: 1 b: 2} | to nuon | metadata | get content_type # 结果:"application/x-nuon"读入失败时的报错信息来自 from nuon 实现 的错误分支,可按消息文本定位原因:
error when loading nuon text/could not load nuon text——外层包装;error when parsing nuon text——语法解析失败;table has mismatched columns——表格行与表头长度不一致;only strings can be keys——表头/键不是字符串;column_defined_twice(即规范中写的error: column_defined_twice)——record 键或表格列名重复;excess values when loading——输入里不止一个顶层值;detected a pipeline in nuon file——输入里出现了管道;variables not supported in nuon、calls not supported in nuon等——NUON 只是数据格式,变量、命令调用、闭包、子表达式都不允许。
限制与边界
注释只在读入时存在:
from nuon不保留注释,to nuon也不会生成注释。带注释的文件经过一次往返后注释会消失。writer 的引号规则:字符串只有在满足“非空、不含强制加引号字符集、不含 Unicode 空白、不含任何十进制数字(类别
Nd)、读回来仍是字符串”时才不加引号。所以a1会被加引号而a-b不会:["a1" "0x" abc "x-1" "a-b" "a_b" "1s" "E5" "3/4"] | to nuon # => ["a1", "0x", abc, "x-1", a-b, a_b, "1s", "E5", "3/4"]规范特别警告:
0x、0o8、0b2这类普通字符串如果裸写,reader 会提交到进制字面量解析然后报错,即产生“自己都打不开的文件”——writer 的加引号逻辑就是为了避免这种情况。duration 和 filesize 的写回形式:writer 对 duration 一律输出
ns([1sec] | to nuon # => [1000000000ns]),filesize 一律输出b([1kb] | to nuon # => [1000b]),读入时的2min、1kb形式只在文件里存在。裸词不能单独成文:
"abc" | from nuon是错误,裸词只能在列表、record 等结构内部使用;顶层单独一个字符串会被 writer 加引号("hi" | to nuon # => "hi",["hi"] | to nuon # => [hi])。不能序列化的类型:closure 默认报错,提示
use --serialize to serialize as a string;custom value 直接报custom values are currently not nuon-compatible。按规范,--serialize实际只对 closure 和 block 生效,binary、cell-path、range 无论是否加该开关都原生写出,glob 无论如何都会变成普通字符串。
完整的语法定义、单位后缀表和逐条的合规检查清单,见 crates/nuon/spec/nuon_formal_specification.md;规范中还维护了 bugs_to_fix.md 追踪当前实现与规范仍不一致的地方,读入行为与上述规则有出入时可以对照该文件。
【免费下载链接】nushellA new type of shell项目地址: https://gitcode.com/GitHub_Trending/nu/nushell
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考