How can I redirect stdout into a file in tcl

前端 未结 7 1447
不思量自难忘°
不思量自难忘° 2020-12-11 16:36

how can I redirect a proc output into a file in tcl, for example, I have a proc foo, and would like to redirect the foo output into a file bar. But got this result



        
相关标签:
7条回答
  • 2020-12-11 16:48

    To accomplish this I wrapped the call to my Tcl proc in a shell script. This works on unix, not sure about other OS.

    file - foo.tcl:

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

    file - foo.csh (any shell script will work, I'm using csh for this example): enter code here

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

    file - main.tcl:

    exec foo.csh > ./myoutput.txt

    Of course these commands can be made 'fancy' by wrapping them in safety measures like catch, etc... for clarity sake I didn't include them, but I would recommend their use. Also I included $argv which isn't needed since proc foo doesn't take args, but typically IRL there will be args. Be sure to use >> if you just want to append to the file rather than overwriting it.

    0 讨论(0)
  • 2020-12-11 16:55

    At present when you type foo > bar Tcl is trying to run a foo proc that takes 2 parameter, as this doesn't exist you get the error message. I can think of two ways you could tackle this problem.

    You can redirect at the top level, so when you run tclsh tclsh > bar then all of the output will be redirected, however I doubt this is what you want.

    You could change foo so that it accepts an open file as a parameter and write to that:

    proc foo {fp} {
       puts $fp "some text"
    }
    
    set fp [open bar w]
    foo $fp
    close $fp
    
    0 讨论(0)
  • 2020-12-11 16:57

    Generally, I'd recommend the stdout redirection that Donal Fellows suggests in his answer.

    Sometimes this may not possible. Maybe the Tcl interpreter is linked into a complex application that has itself a fancy idea of where the output should go to, and then you don't know how to restore the stdout channel.

    In those cases you can try to redefine the puts command. Here's a code example on how you could do that. In plain Tcl, a command can be redefined by renaming it into a safe name, and creating a wrapper proc that calls the original command at the safe name - or not at all, depending on your intended functionality.

    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
    }
    

    You can also redirect text into a variable:

    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
    }
    

    The Tcl'ers Wiki has another interesting example of redefining puts in more complex applications. Maybe this is inspiring as well.

    0 讨论(0)
  • 2020-12-11 17:02

    This should work:

    % 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
    % 
    
    0 讨论(0)
  • 2020-12-11 17:03

    Thanks for sharing CFI. I would like to contribute as well. so, i made some changes to redefine puts on dump to file on startlog and end logging on endlog to file when ever we want. This will print on stdout and also redirect stdout to file.

    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
    }
    
    0 讨论(0)
  • 2020-12-11 17:07

    We can try in this way also

    % 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
    
    0 讨论(0)
提交回复
热议问题