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

时间:2011-12-16 07:21:13

标签: tcl

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

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

7 个答案:

答案 0 :(得分:5)

如果您无法更改代码以获取要写入的频道的名称(最强大的解决方案),则可以使用技巧将stdout重定向到文件:重新开启

proc foo {} { puts "hello world" }
proc reopenStdout {file} {
    close stdout
    open $file w        ;# The standard channels are special
}

reopenStdout ./bar
foo
reopenStdout /dev/tty   ;# Default destination on Unix; Win equiv is CON:

请注意,如果您执行此操作,则会忘记初始stdout的定位位置(除非您使用TclX的dup保存副本)。

答案 1 :(得分:5)

这应该有效:

% 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
% 

答案 2 :(得分:3)

一般来说,我推荐Donal Fellows在his answer中建议的stdout重定向。

有时这可能无法实现。也许Tcl解释器链接到一个复杂的应用程序,它本身就可以了解输出应该去哪里,然后你不知道如何恢复stdout通道。

在这些情况下,您可以尝试重新定义puts命令。这是一个关于如何做到这一点的代码示例。在普通的Tcl中,命令可以由renaming重新定义为一个安全的名称,并创建一个以安全名称调用原始命令的包装器proc - 或根本不调用,具体取决于您的预期功能。

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
}

Tcl'ers Wiki在更复杂的应用程序中有另一个interesting example of redefining puts。也许这也是鼓舞人心的。

答案 3 :(得分:0)

目前当你键入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

答案 4 :(得分:0)

为了实现这一点,我在shell脚本中将调用包装到了我的Tcl proc中。这适用于unix,不确定其他操作系统。

file - foo.tcl:

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

file - foo.csh(任何shell脚本都可以,我在这个例子中使用csh): enter code here

#!/usr/bin/tclsh
source foo.tcl
eval "foo $argv"

file - main.tcl:

exec foo.csh > ./myoutput.txt

当然,这些命令可以制作成“幻想”。通过将它们包裹在捕获等安全措施中...为了清楚起见我没有包含它们,但我建议使用它们。我还包括了$ argv,因为proc foo没有采取args,所以不需要它,但通常IRL会有args。请务必使用&gt;&gt;如果你只想附加到文件而不是覆盖它。

答案 5 :(得分:0)

感谢分享CFI。 我也想做出贡献。 所以,我做了一些更改,重新定义转储到startlog上的文件,并在我们想要的时候结束日志记录到文件。 这将在stdout上打印,并将stdout重定向到文件。

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
}

答案 6 :(得分:0)

我们也可以尝试这种方式

% 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