管道、重定向、错误、日志
九、管道与重定向#
管道#
- 管道简介
lumesh采用和bash一样的管道符,但更加强大:
智能管道
|自动判断应有行为方式,可传输结构化数据。左侧:可以自动从运算结果或标准输出读取;
读取原则: 对于函数和内置命令、运算,读取结构化数据通道。 对于系统三方命令,读取标准输出。
右侧:
输出原则: 如果是三方命令则作为标准输入传入数据 如果是函数则作为末尾参数传递。
| 数据 | 函数、运算、内置命令 | 三方命令 |
|---|---|---|
| 输人(左侧) | 读取结构化数据通道 | 读取标准输出 |
| 输出(右侧) | 输出到最后一个参数 | 输出到标准输入 |
位置管道
|_强制使用参数位置管道,管道到右侧函数的指定位置,以_为占位符,如果未指定,则附加到参数末尾。 多数情况下不需要手动指定。使用|足够。 但如果右侧的命令无法读取标准输入,或需要指定参数位置时,需要使用此管道。13 | print a _ b # 打印结果: a 3 b循环派发管道
|>用于将左侧列表任务,循环派发给右侧命令。 同样支持_占位符。1 0...8 |> print lineNo # 将打印8行 2 ls -1 *.txt |> cp _ /tmp/ # 将拷贝列出的文件PTY管道
|^强制右侧命令使用PTY模式。 某些程序需要完全的终端控制权限,才能正常工作,因此需要pty模式。 智能管道维护着一个此类程序的列表,因此一般无须强制开启pty模式,但如果您发现某个程序无法正常工作,可以尝试强制开启PTY模式。
- 管道基础用法
传统的bash管道为了兼容更多命令,只能处理字节流。字节流是由三方命令输出的文本数据,shell通过管道派发给下一个程序处理。这种模式极大的方便了数据在不同程序之间的传输。
实际上结构化管道的效率更高,因为它省去了从普通文本转化为结构化数据的工作;甚至有的还可以省去和输入输出设备打交到的时间。
结构化管道效率更高 例如:
1# --这是文本流管道--
2echo 3+5 | bat # 这是三方命令,且需要管道从标准输出读取, 效率双重降低
3
4# --这是结构化管道-- *推荐用法*
53+5 | bat # 运算结果直接传送到下一个程序
6
7# --这是不正确用法--
8print 3+5 | bat # print语句打印到标准输出,同时传递出None作为运算结果,bat捕捉到的是print语句的运算结果None
9
10# --这是结构化管道--
11tap 3+5 | bat # tap语句打印到标准输出,同时将结果向下传递,bat正确捕捉到这个运算结果
123+5 | tap | bat # 等价上一句从上面的例子可以看出,
- 运算结果如不需要打印,只需向下传递,应直接使用管道。
- 运算结果如果需要打印,并向下传递,应使用tap后再管道。
- 运算结果打印后不需要向下传递,应使用print。
- 应避免使用echo,除非需要echo -e 等高级选项。
- 管道高级用法
筛选
1# 按大小筛选数据,并显示指定列: 2Fs.ls -l | where(size > 5K) | select(name,size,modified) 3 4# 输出 5+--------------------------------------+ 6| MODIFIED NAME SIZE | 7+======================================+ 8| 2025-06-02 06:26 Cargo.lock 46K | 9| 2025-06-02 04:40 CHANGELOG.md 9K | 10+--------------------------------------+排序
1# 按时间筛选数据,并按指定列排序 2Fs.ls -l | where( Fs.diff('d',modified) > 3 ) | sort(size,name) 3 4# 输出 5+-------------------------------------------------------+ 6| MODE MODIFIED NAME SIZE TYPE | 7+=======================================================+ 8| 511 2025-03-29 05:58 target 11 symlink | 9| 493 2025-04-06 12:21 benches 66 directory | 10| 493 2025-05-13 10:57 assets 102 directory | 11| 493 2025-03-23 11:58 target_ 128 directory | 12| 420 2025-03-23 05:32 LICENSE 1K file | 13| 420 2025-05-29 12:57 README-cn.md 4K file | 14| 420 2025-05-29 12:57 README.md 4K file | 15+-------------------------------------------------------+分组
1# 按类型分组 2Fs.ls -l | group 'type' # type是函数名,所以引号不能省略 3 4+-----------------------------------------------------------------+ 5| KEY VALUE | 6+=================================================================+ 7| directory +--------------------------------------------------+ | 8| | MODE MODIFIED NAME SIZE TYPE | | 9| +==================================================+ | 10| | 493 2025-05-13 10:57 assets 102 directory | | 11| | 493 2025-04-06 12:21 benches 66 directory | | 12| | 493 2025-06-02 05:15 src 346 directory | | 13| | 493 2025-06-03 04:32 wiki 528 directory | | 14| +--------------------------------------------------+ | 15| file +--------------------------------------------------+ | 16| | MODE MODIFIED NAME SIZE TYPE | | 17| +==================================================+ | 18| | 420 2025-03-23 05:32 LICENSE 1K file | | 19| | 420 2025-05-29 12:57 README.md 4K file | | 20| | 420 2025-06-02 06:26 Cargo.lock 46K file | | 21| | 420 2025-06-02 04:40 CHANGELOG.md 9K file | | 22| | 420 2025-06-02 06:26 Cargo.toml 2K file | | 23| +--------------------------------------------------+ | 24| symlink +-----------------------------------------------+ | 25| | MODE MODIFIED NAME SIZE TYPE | | 26| +===============================================+ | 27| | 511 2025-03-29 05:58 target 11 symlink | | 28| +-----------------------------------------------+ | 29+-----------------------------------------------------------------+兼容系统的三方命令
可以使用From.cmd 或 Into.table 或链式调用 .table(),将文本转换结构化数据。可接收列名序列作为参数。
但比较数据时,需要手动转换类型。
1ls -l --time-style=long-iso | .table() | where(int C4>1000)
2
3# 输出
4+------------------------------------------------------------------+
5| C0 C1 C2 C3 C4 C5 C6 C7 |
6+==================================================================+
7| -rw-r--r-- 1 tix tix 10046 2025-06-02 12:40 CHANGELOG.md |
8| -rw-r--r-- 1 tix tix 47312 2025-06-02 14:26 Cargo.lock |
9| -rw-r--r-- 1 tix tix 2226 2025-06-02 14:26 Cargo.toml |
10| -rw-r--r-- 1 tix tix 1075 2025-03-23 13:32 LICENSE |
11| -rw-r--r-- 1 tix tix 4180 2025-05-29 20:57 README-cn.md |
12| -rw-r--r-- 1 tix tix 4333 2025-05-29 20:57 README.md |
13+------------------------------------------------------------------+重定向#
<<读取>>追加输出>!覆盖输出1 + 2 >> result.txt
错误重定向:结合错误处置符 具体用法请参考 错误处理章节。
| 重定向类型 | Lume | Bash |
|---|---|---|
| 标准输出,追加 | cmd >> out.txt | cmd >> out.txt |
| 标准输出,覆盖 | cmd >! out.txt | cmd > out.txt |
| 错误替代标准输出 | cmd ?> >> out.log | command 2>&1 >> out.log |
| 单独的错误输出 | cmd ?? >> out.log | cmd 2>> out.log |
| 合并标准输出和错误输出 | cmd ?+ >> out.log | cmd >> out.log 2>&1 |
十、错误处理#
错误捕获: ?:#
语句后跟 ?: expr,
通常expr 可以是一个lambda函数,该函数可以接受一个Error对象。该Error对象是一个Map对象,可以用
.或@或[]索引,获取具体属性。 该对象包含三个属性:codemsgexpr
如果expr是常规类型,则在异常时提供默认值。
错误忽略: ?.#
相当于 ?: {}
错误打印: ?+ ?? ?>#
错误打印到标准输出:
?+相当于?: e -> echo e.msg错误打印到错误输出:
??相当于?: e -> eprint e.msg错误作为运算结果输出:
?>相当于?: e -> e.msg
错误终止: ?!#
- 遇到错误时,静默终止管道
Fs.ls -l | ui.pick ?! |_ cp _ /tmp
当用户放弃选择时,遇错终止后续管道操作。
以上方式可以作用于任何语句或函数末尾。 错误处置符的优先级高于管道。
1
26 / 0 ?. # ignore err
36 / 0 ?+ # print err to stdout
46 / 0 ?? # print err to stderr
56 / 0 ?! # use this err msg as result
6
7let e = x -> echo x.code
86 /0 ?: e # deeling the err with a function/lambda
9
10# also funcions could use err handling too.
11fn divide(x,y){
12 x / y
13}?: eTips
利用错误处置符的特性,还可以为计算失败时提供默认值:
1let e = x -> 0 2let result = 6 /0 ?: e 3echo result # 输出默认值: 0 4 5# 等同于 6let result = 6 /0 ?: 0 7echo result # 输出默认值: 0在函数定义时,使用错误处理
1let e = x -> {print x.code} 2 3fn div(a,b){ 4 a / b 5} ?: e 6 7div(3,0) # the defined error handling will be executed here.
错误调试:#
错误提示 一般情况下,观察错误提示,足以发现问题所在。 如:
1[PARSE FAILED] # 语法解析错误 2unexpected syntax error: expect "{" 3 ▏ 4 1 ▏ fn assert(actual, expected, test_name, test_count=0) { 5 2 ▏ if actual != expected 6 ▏ ^ 7 3 ▏ print "[FAIL]" test_count test_name "| 实际:" actual "| 预期" expected 8 4 ▏ } else { 9 5 ▏ print "[PASS]" test_count test_name 10 ▏ 11 ↳ at line 2, column 27 12 13 14> 0...8 x 15[ERROR] # 运行时错误 16Message [1]: type error, expected Symbol as command, found 0...8: List 17Expression[1]: 0...8 x 18 19SyntaxTree[1]: 20Cmd 〈0...8〉 21〖 22 Symbol〈"x"〉 23〗debug命令 如需进一步调试,可以使用debug命令。1# 简单数据调试: 2let c = 0..8 3debug c # 输出:Range〈0..9,1〉 4 5# 复杂语句调试: 6let a := if a {3 + 5} else { print a is False} 7debug a 8 9# 输出: 10if (Symbol〈"a"〉) { 11 12 { 13 14 Integer〈3〉 15 + 16 Integer〈5〉 17 } 18 19}else{ 20 21 { 22 23 Cmd 〈Symbol〈"print"〉〉 24 〖 25 Symbol〈"a"〉 26 Symbol〈"is"〉 27 Boolean〈false〉 28 〗 29 30 } 31 32}Log 模块 Log模块的使用也有利于错误调试,具体请参考Log模块文档