You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nushell测试脚本中将闭包的stdout和stderr重定向至变量?

Nushell测试脚本:将闭包的stdout/stderr捕获到变量替代临时文件

问题描述

我正在为一款CLI编写Nushell测试脚本,核心需求是仅在测试函数执行失败时,打印其stdout、stderr以及Nushell的错误信息。目前的实现是将输出写入临时文件后再读取,效率较低,希望能直接将闭包的stdout和stderr重定向到变量,但尝试了多种方法均未成功。

当前实现

def run_tests [functions, passed, total] -> int {
    mut passed = $passed

    for pair in ($functions | enumerate) {
        print $"test ($pair.index): running" --no-newline
        try { 
            do $pair.item.func out+err> /tmp/test-output # FIXME: 临时文件方案效率低
            print $"\rtest ($pair.index): success"
            $passed += 1
        } catch {
            print $"\rtest ($pair.index): failure"
            print (open /tmp/test-output)
            print $in
        }
    }
    return $passed
}

注:$functions是包含func列的表格,func列存储了带有断言、可能执行失败的Nushell命令及外部命令的闭包。

已尝试的方法

  • let result = do $pair.item.func:直接打印闭包输出,不符合“仅失败时打印”的需求
  • complete:仅支持外部命令,对Nushell闭包无效
  • do $pair.item.func out+err> $result:语法不支持向变量重定向,仅能写入文件

测试用例示例

let functions = [
    [name, func];
    [start-stop-label, {
        log-timer start test
        log-timer stop
        log-timer get logs
        assert (log-timer get logs | str contains test)
        }
    ],
    [overwrite-options, {
        touch ~/log-1.csv
        log-timer config set --log-file-path log-1.csv --row-formatter v1-0
        let config = log-timer config get | from json
        assert ($config.log_file_path == ('~/log-1.csv' | path expand))
        assert ($config.row_formatter == V1_0)

        log-timer config set --row-formatter v2-0
        let config = log-timer config get | from json
        assert ($config.log_file_path == ('~/log-1.csv' | path expand))
        assert ($config.row_formatter == V2_0)

        touch ~/log-2.csv
        log-timer config set --log-file-path ~/log-2.csv
        let config = log-timer config get | from json
        assert ($config.log_file_path == ('~/log-2.csv' | path expand))
        assert ($config.row_formatter == V2_0)
    },],
    [set-default, {
        log-timer config set-default --confirm
        let current = log-timer config get | from json
        let default = log-timer config get-default | from json
        assert ($current == $default)
    }],
]

期望输出(start-stop-label测试失败时)

test 1: failure
started activity 'test'
stopped activity 'test'
Error:   × Assertion failed.
   ╭─[entry #4:1:8]
 1 │ assert (log-timer get logs | str contains test)
   ·        ──┬──
   ·          ╰── It is not true.
   ╰────
test 2: success
test 3: running

解决方案

方案1:单次执行捕获输出与错误

利用Nushell的错误捕获机制,在捕获错误的同时收集闭包的输出,无需临时文件:

def run_tests [functions, passed, total] -> int {
    mut passed = $passed

    for pair in ($functions | enumerate) {
        print $"test ($pair.index): running" --no-newline
        
        let capture_result = (
            try {
                # 执行闭包,若成功则记录状态
                do $pair.item.func
                { status: "success", output: "" }
            } catch {
                # 执行闭包并捕获所有输出
                let captured_output = (do $pair.item.func out+err> stdout) | str collect
                { status: "failure", output: $captured_output, error: $in }
            }
        )

        if $capture_result.status == "success" {
            print $"\rtest ($pair.index): success"
            $passed += 1
        } else {
            print $"\rtest ($pair.index): failure"
            print $capture_result.output
            print $capture_result.error
        }
    }
    return $passed
}

方案2:后台任务捕获输出(无重复执行)

如果测试闭包有副作用,不适合重复执行,可以用后台任务捕获输出并同步获取执行状态:

def run_tests [functions, passed, total] -> int {
    mut passed = $passed

    for pair in ($functions | enumerate) {
        print $"test ($pair.index): running" --no-newline
        
        # 启动后台任务执行闭包,捕获所有输出
        let job = (do $pair.item.func out+err> stdout) | start
        wait $job
        let captured_output = ($job | get stdout) | str collect
        let exit_code = $job | get exit_code

        if $exit_code == 0 {
            print $"\rtest ($pair.index): success"
            $passed += 1
        } else {
            # 重新捕获Nushell的错误信息(若需要完整错误栈)
            try { do $pair.item.func } catch {
                print $"\rtest ($pair.index): failure"
                print $captured_output
                print $in
            }
        }
    }
    return $passed
}

注:方案2中若不需要完整的Nushell错误栈,可直接通过exit_code判断并打印捕获的输出,无需再次执行闭包。

内容的提问来源于stack exchange,提问作者Ask Sødal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:08:09