【问题标题】:Tcl Expect "interact" command echos previous output from spawned rlwrap processTcl Expect "interact" 命令从生成的 rlwrap 进程回显先前的输出
【发布时间】:2013-10-17 22:20:16
【问题描述】:

我感觉我缺少一些明显的东西,但到目前为止我的搜索没有结果。

我正在尝试使用 tcl/expect 脚本来启动 tclsh 交互式 shell,添加一个用于轻松重新加载实用程序以进行测试的过程,然后将正常控制权返回给我。

到目前为止,我发现让 tcl 交互式 shell “可用”的一种方法是用“rlwrap”启动它,这样我就可以使用箭头键等。

所以我尝试了以下脚本,当交互命令被命中时,rlwrap 的某些内容导致以前的输出被转储到标准输出。

我能做些什么来避免这种情况发生吗?

代码:

package require Expect


puts "Tcl version   : [info tclversion]"
puts "Expect version: [exp_version]"


log_user 0
spawn -noecho rlwrap tclsh

# Create procedure to easily reload utilites after changes have been made
expect "% "
send {
   proc reload {} {
      # Procedure to reload utility source easily for testing
   }
}

# Source utilities
expect "% "
send "reload\r"

send_user "\nUse 'reload' procedure to re-source utility files\n\n"

log_user 1
interact

输出:

Tcl version   : 8.4
Expect version: 5.43.0

Use 'reload' procedure to re-source utility files


   proc reload {} {
      # Procedure to reload utility source easily for testing
   }
% reload
%  

你可以因为某种原因它呼应 proc 定义和重新加载命令的输入。一旦发生交互,就会发生这种情况。如果我将交互替换为“退出”,我将看不到任何输出。

当然我希望看到的输出是这样的:

Tcl version   : 8.4
Expect version: 5.43.0

Use 'reload' procedure to re-source utility files

%  

【问题讨论】:

  • 试试expect "% " 之前 log_user 1
  • Glenn 的建议很好,但不幸的是它似乎并没有改变输出。
  • 发送proc后不发送回车。
  • 我对此进行了实验,但似乎一点也不重要。不知道为什么,但它按预期工作(除了额外显示的输出)。 proc 被正确定义并且一旦交互允许我控制就可以使用。

标签: tcl expect


【解决方案1】:

如果你不介意自己编译一个小的 C 程序,你可以使用这个:

#include <tcl.h>

#ifdef WIN32
#ifdef UNICODE
#define WIN32_UNICODE
#endif
#endif

int TclSHI_Main(Tcl_Interp*);

static int g_argc;
#ifdef WIN32_UNICODE
#define Tcl_NewStringObj Tcl_NewUnicodeObj
static wchar_t*** g_argv;
void wmain(int argc, wchar_t **argv) {
#else
static char*** g_argv;
void main(int argc, char **argv) {
#endif
    g_argc = argc;
    g_argv = &argv;
    Tcl_FindExecutable(argv[0]);
    Tcl_Main(1, argv, TclSHI_Main);
}

int TclSHI_Main(Tcl_Interp* interp) {
    Tcl_Obj* lobj;
    int i;
    if (g_argc > 1) {
        Tcl_SetVar2Ex(interp, "argv0", NULL, Tcl_NewStringObj((*g_argv)[1], -1), TCL_GLOBAL_ONLY);
    }
    lobj = Tcl_NewObj();
    Tcl_IncrRefCount(lobj);
    for (i = 2; i < g_argc; i++) {
        Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj((*g_argv)[i], -1));
    }
    Tcl_SetVar2Ex(interp, "argv", NULL, lobj, TCL_GLOBAL_ONLY);
    Tcl_DecrRefCount(lobj);
    Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(g_argc - 2), TCL_GLOBAL_ONLY);
    if (g_argc > 1) {
        Tcl_Eval(interp, "source $argv0");
    }
    return TCL_OK;
}

我在 windows (CL) 和 linux (GCC) 上对其进行了测试。
为了用 gcc 编译它,我使用了gcc TclSH.c -o TclSHI -ltcl8.6
在 Windows 上,我使用了 Visual Studio。

它告诉 Tcl 它没有收到任何参数 (Tcl_Main(1,...)),但是用这个参数填充新的 interp 并获取文件。在这一步之后,它总是会显示提示(它从未收到任何参数,对吧?)。

您的期望解决方案有一个小问题,如果您指定任何参数,Tcl 将执行该脚本,并且从不显示提示。

另外请注意,我是一名新手 C 程序员,所以这个解决方案可能不是万无一失的。

【讨论】:

  • 感谢 Johannes 的代码。这是我一定会研究的一种可能的解决方法。但是对于这个问题,我仍然试图了解 rlwrap 和期望之间的问题。本质上,这是解决问题和真正理解问题之间的区别。
  • 你是绝对正确的,传递和争论是行不通的。这只是我为这篇文章推断的一个简单示例。我会编辑它,谢谢。
【解决方案2】:

您要做的是等待一个明确的标记,表明从属进程已准备就绪。

# ... your script as above ...
expect "% "

#### NEW STUFF STARTS ####
send "reload;puts READY\r"
expect "READY\r"

# Note that we need to fake the prompt; c'est la vie
send_user "\nUse 'reload' procedure to re-source utility files\n\n% "

# Now start doing things!
log_user 1
interact

或者至少当我尝试使用从属进程时这有效,但我没有在混合中使用 rlwrap,所以这可能会改变事情......

【讨论】:

  • 唯一的其他方法是编写一个使用Tcl_Main 的自定义C 应用程序或自己编写输入/评估/输出循环。我相信我们需要一个tclsh -interactive initscript.tcl
  • 可以使用rlwrap tclsh -interactive initscript.tcl
  • @JohannesKuhn,我实际上搜索了一个到 tclsh 的 -interactive 开关,因为我认为必须有这样的东西。唉,没有,但我同意当我只是一个带有初始化脚本的 tclsh 时,添加它以避免使用期望是一件好事。
  • @DonalFellows,我尝试了你的建议,它改变了行为,但没有解决它。现在的问题是它的行为不一致......有时我看到 proc 定义,有时我没有。我多次运行同一个脚本,它的行为随机不同。一开始我以为它已经改进了,但它恰好是一个迭代,它没有输出 proc 定义。
猜你喜欢
  • 2012-10-27
  • 1970-01-01
  • 2018-01-16
  • 2015-03-19
  • 2013-02-17
  • 1970-01-01
  • 2015-07-18
  • 2013-07-28
  • 2014-06-18
相关资源
最近更新 更多