在Tcl中跟踪stdout和stderr

时间:2015-03-23 12:19:49

标签: tcl stdout trace stderr

我不确定这是否荒谬。是不是trace可以在Tcl中读写stdoutstderr

我已经尝试了下面的内容,但没有发现任何线索。

% proc tracer {varname args} {
    upvar #0 $varname var
    puts "$varname value : $var"
}
% trace add variable stdout read tracer
% trace add variable stdout write tracer
% puts stdout hai
hai
% puts hai
hai
% trace add variable stderr write tracer
% trace add variable stderr read tracer
% puts hai
hai
% puts stderr hai
hai

根据puts的手册页,如果没有为channelId命令指定puts,则默认为stdout,即使puts hai也是如此,stdout将被访问。对 ? (虽然即使参数为stdoutstderr

也不起作用

1 个答案:

答案 0 :(得分:1)

您尝试的解决方案存在的问题是,stdoutstderrstdin不是变量,而是所谓“渠道”的名称。 本质上,它们位于一个独立的命名空间(不是由namespace Tcl命令操作的命名空间!):您可以使用chan names命令获取它们的列表,但是您不能, rename一个频道,或为其分配一个值或unset它:这些操作对频道没有任何意义,而是会影响该名称的变量。

跟踪渠道的方法是用另一个“脚本级” - 渠道和“代理”所有操作实际上颠覆它。这个技巧使用了一个鲜为人知的Tcl功能:当你关闭一个Tcl的标准通道并立即打开一个通道(无论是“真实”还是“脚本级别”)时,它将被注册以代替刚刚关闭的标准通道。因此,如果我们关闭一个标准频道并立即在其位置创建我们自己的“代理”频道,我们就会破坏该标准频道。

那些“脚本级”(“反射”)通道要求Tcl≥8.5。

这是一个颠覆stdout的草图,需要Linux(/proc/self/fd/<fileno>支持)。

proc traceChan {cmd chan args} {
    global stdout

    puts stderr "Trace on $chan; cmd=$cmd; args=$args"

    switch -- $cmd {
        initialize {
            return [list initialize finalize watch write configure cget cgetall]
        }
        finalize {
            chan close $stdout
        }
        watch {
            # FIXME: not implemented
        }
        write {
            set data [lindex $args 0]
            chan puts -nonewline $stdout $data
            return [string length $data]
        }
        configure {
            return [chan config $stdout {*}$args]
        }
        cget {
            return [chan cget $stdout {*}$args
        }
        cgetall {
            return [chan configure $stdout]
        }
    }
}

set fn [file readlink /proc/self/fd/1]
set conf [chan config stdout]
chan close stdout
chan create write ::traceChan
set stdout [open $fn w]
chan configure stdout {*}$conf

puts [chan names]
puts test
chan flush stdout
chan close stdout

在我的系统上,它会破坏终端设置(stdout连接到终端),并且要求我在脚本退出后执行reset后跟stty sane,但至少它会执行完成工作。

此脚本的实际问题:

  • 实际上我们对写入的字节数有所说明:我们不知道底层stdout通道会写多少字节,因为它取决于很多事情。
  • 我们根本不处理频道事件。

通过编写实现这种代理的C模块可以解决这些问题:使用C API,您可以访问基础文件描述符/句柄(因此不需要/proc/self/fd/...)和Tcl对象包装它(所以你可能只是克隆它)并知道底层通道写了多少字节。


哦,请注意,如果您将脚本发送的数据丢弃到颠覆的标准频道,那么就不要重新打开真正的底层文件,关闭它,向它写入数据等等。然后,解决方案将在chan create之后立即执行chan close并在跟踪过程中跟踪写入。在响应write调用时,您的跟踪例程仍然需要返回丢弃的字节数。


另请阅读chanrefchan手册页。