如何将stdout重定向到tcl中的文件中

如何将stdout重定向到tcl中的文件中,tcl,Tcl,如何将proc输出重定向到tcl中的文件中,例如,我有一个proc foo,并且希望将foo输出重定向到文件栏中。但是得到了这个结果 % proc foo {} { puts "hello world" } % foo hello world % foo > bar wrong # args: should be "foo" 目前,当您键入foo>barTcl试图运行一个带有2个参数的foo proc时,由于该参数不存在,您会收到错误消息。我可以想出两种方法来解决这个问题 您可以在顶层重

如何将proc输出重定向到tcl中的文件中,例如,我有一个proc foo,并且希望将foo输出重定向到文件栏中。但是得到了这个结果

% proc foo {} { puts "hello world" }
% foo
hello world
% foo > bar
wrong # args: should be "foo"

目前,当您键入
foo>bar
Tcl试图运行一个带有2个参数的foo proc时,由于该参数不存在,您会收到错误消息。我可以想出两种方法来解决这个问题

您可以在顶层重定向,因此当您运行tclsh
tclsh>bar
时,所有输出都将被重定向,但我怀疑这是否是您想要的

您可以更改foo,使其接受打开的文件作为参数,并写入该文件:

proc foo {fp} {
   puts $fp "some text"
}

set fp [open bar w]
foo $fp
close $fp

如果您无法将代码更改为要写入的通道的名称(最健壮的解决方案),则可以使用技巧将
stdout
重定向到文件:重新打开


请注意,如果您这样做,您将无法知道初始的
stdout
指向何处(除非您使用TclX保存副本)。

一般来说,我建议使用Donal Fellows在中建议的stdout重定向

有时这可能是不可能的。也许Tcl解释器被链接到一个复杂的应用程序中,这个应用程序本身就有一个关于输出应该去哪里的奇特想法,然后您就不知道如何恢复标准输出通道

在这些情况下,您可以尝试重新定义
put
命令。下面是一个代码示例,说明如何做到这一点。在纯Tcl中,可以将命令重新定义为安全名称,并创建一个包装程序,以安全名称调用原始命令,或者根本不调用,具体取决于您预期的功能

proc redirect_file {filename cmd} {
    rename puts ::tcl::orig::puts

    set mode w
    set destination [open $filename $mode]

    proc puts args "
        uplevel \"::tcl::orig::puts $destination \$args\"; return
    "

    uplevel $cmd

    close $destination

    rename puts {}
    rename ::tcl::orig::puts puts
}
您还可以将文本重定向到变量中:

proc redirect_variable {varname cmd} {
    rename puts ::tcl::orig::puts

    global __puts_redirect
    set __puts_redirect {}

    proc puts args {
        global __puts_redirect
        set __puts_redirect [concat $__puts_redirect [lindex $args end]]
        set args [lreplace $args end end]
        if {[lsearch -regexp $args {^-nonewline}]<0} {
            set __puts_redirect "$__puts_redirect\n"
        }
        return
    }

    uplevel $cmd

    upvar $varname destination
    set destination $__puts_redirect
    unset __puts_redirect

    rename puts {}
    rename ::tcl::orig::puts puts
}
proc redirect_变量{varname cmd}{
重命名put::tcl::orig::put
全局\uuuu放置\uu重定向
集合{u放置{u重定向}
proc puts args{
全局\uuuu放置\uu重定向
设置放置重定向[concat$放置重定向[lindex$args end]]
设置参数[lreplace$args end]
如果{[lsearch-regexp$args{^-nonewline}]这应该可以:

% proc foo {} { return "hello world" }
% foo
hello world
% exec echo [foo] > bar
% exec cat bar
hello world
% exec echo [foo] >> bar
% exec cat bar
hello world
hello world
% 

为了实现这一点,我将对我的Tcl proc的调用包装在一个shell脚本中。这可以在unix上工作,但不确定其他操作系统

文件-foo.tcl:

proc foo {} {puts "Hi from foo"}

文件-main.tcl:

exec foo.csh > ./myoutput.txt exec foo.csh>/myoutput.txt 当然,这些命令可以通过将它们包装在诸如catch等安全措施中而变得“花哨”。为了清楚起见,我没有包括它们,但我建议使用它们。此外,我还包括$argv,这是不需要的,因为proc foo不接受args,但通常IRL会有args。如果您只想附加到文件r,请确保使用>>a而不是覆盖它。

谢谢分享CFI。 我也愿意作出贡献。 所以,我做了一些修改,在startlog上重新定义puts on dump到文件,并在需要时结束endlog到文件的日志记录。 这将在标准输出上打印,并将标准输出重定向到文件

proc startlog {filename } {
    rename puts ::tcl::orig::puts

    set mode w
    global def destination logfilename
    set destination [open $filename $mode]
    set logfilename $filename

    proc puts args "
        uplevel 2 \"::tcl::orig::puts \$args\"
        uplevel \"::tcl::orig::puts $destination \{\$args\}\"; return
    "
}

proc endlog { } {
    global def destination logfilename  
    close $destination
    rename puts {}
    rename ::tcl::orig::puts puts
    cleanlog $logfilename
    puts "name of log file is $logfilename"
}

proc cleanlog {filename } {  
    set f [open $filename]
    set output [open $filename\.log w]
    set line1 [regsub -all {\-nonewline stdout \{} [read $f] ""]
    set line2 [regsub -all {stdout \{} $line1 ""]
    set line3 [regsub -all {\}} $line2 ""]
    set line4 [regsub -all {\{} $line3 ""]
    puts $output $line4
    close $output
    file rename -force $filename.log $filename
}

我们也可以这样尝试

% proc foo {} { return "hello world" }

% foo

hello world

% set fd [open "a.txt" w]

file5

% set val [foo]

hello world

% puts $fd $val

% close $fd

% set data [exec cat a.txt]

hello world
proc startlog {filename } {
    rename puts ::tcl::orig::puts

    set mode w
    global def destination logfilename
    set destination [open $filename $mode]
    set logfilename $filename

    proc puts args "
        uplevel 2 \"::tcl::orig::puts \$args\"
        uplevel \"::tcl::orig::puts $destination \{\$args\}\"; return
    "
}

proc endlog { } {
    global def destination logfilename  
    close $destination
    rename puts {}
    rename ::tcl::orig::puts puts
    cleanlog $logfilename
    puts "name of log file is $logfilename"
}

proc cleanlog {filename } {  
    set f [open $filename]
    set output [open $filename\.log w]
    set line1 [regsub -all {\-nonewline stdout \{} [read $f] ""]
    set line2 [regsub -all {stdout \{} $line1 ""]
    set line3 [regsub -all {\}} $line2 ""]
    set line4 [regsub -all {\{} $line3 ""]
    puts $output $line4
    close $output
    file rename -force $filename.log $filename
}
% proc foo {} { return "hello world" }

% foo

hello world

% set fd [open "a.txt" w]

file5

% set val [foo]

hello world

% puts $fd $val

% close $fd

% set data [exec cat a.txt]

hello world