0

我需要使用 Tcl expect 来自动化一个巨大的交互式 Tcl 程序。

正如我意识到的那样,这个领域真的很危险,因为我需要扩展已经存在的大量代码,但我不能像在常规脚本中那样依赖实际上导致程序失败并显示正退出代码的错误。这意味着我必须考虑所有可能出错的事情并“期待”它。

我目前所做的是使用“死”过程而不是在我自己的代码中引发错误,该错误会自动退出。但是这种错误情况是无法catch编辑的,并且很难检测到错误,尤其是在不是我编写的代码中,因为最终,大多数库例程都将error基于 -。

由于我可以访问程序的 Tcl shell,是否可以启用错误时失败?

编辑:

我正在使用 Tcl 8.3,就可用工具而言,这是一个严重的限制。

我想自动退出的错误示例:

% puts $a(2)
can't read "a(2)": no such element in array
while evaluating {puts $a(2)}
%

% blublabla
invalid command name "blublabla"
while evaluating blublabla
% 

以及使正常脚本终止的任何其他错误。这些可以从过程调用的 10 层深处冒出来。

我也尝试重新定义全局error命令,但并非 Tcl 中可能发生的所有错误都使用它。例如,上面的“找不到命令”错误没有通过我的自定义error过程。

4

1 回答 1

0

由于我可以访问程序的 Tcl shell,是否可以启用错误时失败?

让我试着用我的话来总结一下:您想在出错时退出交互式 Tcl shell,而不是再次提供提示?

更新

我正在使用 Tcl 8.3,就可用工具而言,这是一个严重的限制 [...] 只有 C 代码的源补丁。

由于您似乎深陷于那个兔子洞,为什么不添加另一个源补丁呢?

--- tclMain.c   2002-03-26 03:26:58.000000000 +0100
+++ tclMain.c.mrcalvin  2019-10-23 22:49:14.000000000 +0200
@@ -328,6 +328,7 @@
        Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
        Tcl_WriteChars(errChannel, "\n", 1);
        }
+       Tcl_Exit(1);
    } else if (tsdPtr->tty) {
        resultPtr = Tcl_GetObjResult(interp);
        Tcl_GetStringFromObj(resultPtr, &length);

这是未经测试的,Tcl 8.3.5 源不为我编译。但是这部分Tcl的内部和现在的源码相当,使用我的Tcl 8.6源码安装测试。

对于记录

有了股票壳(tclsh),这恐怕有点繁琐。以下可能对您有用(不过,我可以想象这可能会让您失望的情况)。这个想法是

  • 拦截写入stderr(这是交互式 shell 在返回提示之前重定向错误消息的位置)。
  • 为了区分任意写入stderr和错误情况,可以使用全局变量::errorInfo作为哨兵。

第 1 步:定义通道拦截器

oo::class create Bouncer {
    method initialize {handle mode} {
        if {$mode ne "write"} {error "can't handle reading"}
        return {finalize initialize write}
    }
    method finalize {handle} {
        # NOOP
    }

    method write {handle bytes} {
        if {[info exists ::errorInfo]} {
        # This is an actual error;
        # 1) Print the message (as usual), but to stdout
        fconfigure stdout -translation binary
        puts stdout $bytes
        # 2) Call on [exit] to quit the Tcl process
        exit 1
    } else {
        # Non-error write to stderr, proceed as usual
            return $bytes
    }
    }
}

stderr第 2 步:在交互式 shell 中注册拦截器

if {[info exists ::tcl_interactive]} {
    chan push stderr [Bouncer new]
}

注册后,这将使您的交互式 shell 的行为如下:

% puts stderr "Goes, as usual!"
Goes, as usual!
% error "Bye, bye"
Bye, bye

一些备注

  • 您需要小心Bouncer'swrite方法,错误消息已经针对字符编码进行了按摩(因此,fconfigure调用)。
  • 您可能希望将其放入 Tcl 包或 Tcl 模块中,以使用package req.
  • 我可以想象您的程序写入stderr并且errorInfo变量恰好被设置(作为剩余),这将触发意外退出。
于 2019-10-22T13:33:28.167 回答