在 Tcl 中跟踪标准输出和标准输出



我不确定这是否荒谬。在Tcl中trace 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 的手册页,如果没有为puts命令指定channelId,则默认为 stdout这意味着即使使用 puts haistdout 也会被访问。右?(尽管即使参数为stdoutstderr,它也不起作用(

您尝试的解决方案的问题在于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 close和跟踪写入后立即执行chan create。 为了响应write调用,您的跟踪例程仍需要返回丢弃的字节数。


另请阅读chanrefchan手册页。

相关内容

  • 没有找到相关文章

最新更新