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