在Tcl中跟踪stdout和stderr

在Tcl中跟踪stdout和stderr,tcl,stdout,trace,stderr,Tcl,Stdout,Trace,Stderr,我不确定这是否荒谬。在Tcl中是否可以跟踪标准输出和标准输出的读写 我试过下面的方法,没有发现任何线索 % 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 % p

我不确定这是否荒谬。在Tcl中是否可以
跟踪
标准输出
标准输出
的读写

我试过下面的方法,没有发现任何线索

% 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 hai
,也将访问
stdout
。正确的?(尽管它甚至不能与参数
stdout
stderr
一起工作)

您尝试的解决方案的问题是
stdout
stderr
stdin
不是变量,而是所谓“通道”的名称。 本质上,它们位于一个单独的名称空间中(不是由
名称空间
Tcl命令操作的名称空间!):您可以使用
chan names
命令获得它们的列表,但是您不能,比如说,
重命名
频道,或者给它赋值或取消设置:这些操作在通道上没有意义,而是通过该名称影响变量

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

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

下面是subversing
stdout
的简图,它需要Linux(用于
/proc/self/fd/
支持)

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

此脚本的实际问题:

  • 事实上,我们对写入的字节数撒谎:我们不知道底层的
    stdout
    通道将写入多少字节,因为它取决于许多事情
  • 我们根本不处理频道事件
这些问题可以通过编写实现这种代理的C模块来解决:使用C API,您可以访问底层文件描述符/句柄(因此不需要
/proc/self/fd/…
)和包装它的Tcl对象(因此您可以立即克隆它)并知道底层通道写入了多少字节


哦,请注意,如果您可以将脚本发送的数据丢弃到被破坏的标准通道,只需重新打开真正的底层文件,关闭它,向其写入数据等。然后解决方案将归结为在
chan close
之后立即执行
chan create
,并在跟踪过程中进行跟踪写入。为了响应
write
调用,您的跟踪例程仍然需要返回丢弃的字节数



还请阅读和手册页面。

通过推动日志转换(Tcl 8.6功能)进行拦截是可能的。而且更干净。嗯,
stdout
stderr
是通道,而不是变量。。。我不完全确定你想做什么。
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