我有一个变量,让我们说xx
,带有索引0和索引1值的列表。我想修改一个先前定义了函数pptable
的脚本(不是我的),即
proc pptable {l1 l2} {
foreach i1 $l1 i2 $l2 {
puts " [format %6.2f $i1]\t[format %6.2f $i2]"
}
}
以便使用
将输出显示为两列pptable [lindex $xx 1] [lindex $xx 0]
但是,我想将输出直接写入文件。你能告诉我如何将数据发送到文件而不是显示器吗?
答案 0 :(得分:2)
执行此操作的最佳方法之一是stack上的channel transform将stdout
重定向到您希望的位置。即使对stdout
的写入发生在C代码或不同的线程中,因为它插入到通道机制中,这也会起作用。代码有点长(并且需要Tcl 8.6)但是可靠且实际上非常简单。
package require Tcl 8.6; # *REQUIRED* for [chan push] and [chan pop]
proc RedirectorCallback {targetHandle op args} {
# The switch/lassign pattern is simplest way of doing this in one procedure
switch $op {
initialize {
lassign $args handle mode
# Sanity check
if {$mode ne "write"} {
close $targetHandle
error "this is just a write transform"
}
# List of supported subcommands
return {initialize finalize write}
}
finalize {
lassign $args handle
# All we need to do here is close the target file handle
close $targetHandle
}
write {
lassign $args handle buffer
# Write the data to *real* destination; this does the redirect
puts -nonewline $targetHandle $buffer
# Stop the data going to *true* stdout by returning empty string
return ""
# If we returned the data instead, this would do a 'tee'
}
default {
error "unsupported subcommand"
}
}
}
# Here's a wrapper to make the transform easy to use
proc redirectStdout {file script} {
# Stack the transform onto stdout with the file handle to write to
# (which is going to be $targetHandle in [redirector])
chan push stdout [list RedirectorCallback [open $file "wb"]]
# Run the script and *definitely* pop the transform after it finishes
try {
uplevel 1 $script
} finally {
chan pop stdout
}
}
我们如何实际使用它?在实践中它真的很容易:
# Exactly the code you started with
proc pptable {l1 l2} {
foreach i1 $l1 i2 $l2 {
puts " [format %6.2f $i1]\t[format %6.2f $i2]"
}
}
# Demonstrate that stdout is working as normal
puts "before"
# Our wrapped call that we're capturing the output from; pick your own filename!
redirectStdout "foo.txt" {
pptable {1.2 1.3 1.4} {6.9 6.8 6.7}
}
# Demonstrate that stdout is working as normal again
puts "after"
当我运行该代码时,我得到了这个:
bash$ tclsh8.6 stdout-redirect-example.tcl before after bash$ cat foo.txt 1.20 6.90 1.30 6.80 1.40 6.70
我相信这正是您所寻找的。 p>
如果您使用Tcllib和TclOO帮助处理机器,则可以使用更少的代码执行此操作:
package require Tcl 8.6
package require tcl::transform::core
oo::class create WriteRedirector {
superclass tcl::transform::core
variable targetHandle
constructor {targetFile} {
set targetHandle [open $targetFile "wb"]
}
destructor {
close $targetHandle
}
method write {handle buffer} {
puts -nonewline $targetHandle $buffer
return ""
}
# This is the wrapper, as a class method
self method redirectDuring {channel targetFile script} {
chan push $channel [my new $targetFile]
try {
uplevel 1 $script
} finally {
chan pop $channel
}
}
}
用法示例:
proc pptable {l1 l2} {
foreach i1 $l1 i2 $l2 {
puts " [format %6.2f $i1]\t[format %6.2f $i2]"
}
}
puts "before"
WriteRedirector redirectDuring stdout "foo.txt" {
pptable {1.2 1.3 1.4 1.5} {6.9 6.8 6.7 6.6}
}
puts "after"
答案 1 :(得分:0)
我认为你不想或不能修改现有的脚本和proc pptable
,对吗?
如果是这样,根据您的具体情况,有不同的选项:
stdout
:tclsh yourscript.tcl > your.out
重新定义puts
(对于明确定义的范围):
rename ::puts ::puts.orig
proc puts args {
set fh [open your.out w];
::puts.orig $fh $args;
close $fh
}
# run pptable, source the script
之前已涵盖此主题,例如tcl stop all output going to stdout channel?
重新连接Tcl的stdout
频道(不一定推荐):
close stdout
open your.out w
# run pptable, source the script
之前已经详细阐述过,例如: Tracing stdout and stderr in Tcl