• Send output to terminal and file

    From Cecil Westerhof@21:1/5 to All on Tue Dec 7 14:26:09 2021
    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to Cecil Westerhof on Tue Dec 7 14:55:19 2021
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ted Nolan @21:1/5 to Cecil@decebal.nl on Tue Dec 7 14:39:07 2021
    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update.
    Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f
    --
    columbiaclosings.com
    What's not in Columbia anymore..

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to ted@loft.tnolan.com on Tue Dec 7 15:43:11 2021
    Ted Nolan <tednolan> <ted@loft.tnolan.com> wrote:
    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update. Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    Or, this is tcl.... (likely better done as an object to avoid use of a
    global)

    proc tee-init {filename} {
    set ::teefd [open $filename {WRONLY CREAT TRUNC}]
    # fconfigure $::teefd ....
    }

    proc tee {args} {
    puts [join $args]
    puts $::teefd [join $args]
    }

    Then, instead of "puts ..." do:

    tee "what you want to output"
    or
    tee this is $something to output

    The second version is due to the [join $args] method of output.

    And... if one /really/ wants to, one can rename puts and create one's
    own 'puts' that internally does a 'tee'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to ted@loft.tnolan.com on Tue Dec 7 17:48:10 2021
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update. Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    Yes, I am not completely happy about it.
    The problem with your solution is that even when there is no output
    the file is created, or the file-stamp changed.
    (Both is not what I want.)

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ted Nolan @21:1/5 to Cecil@decebal.nl on Tue Dec 7 17:04:27 2021
    In article <87wnkgl5th.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update. >> Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    Yes, I am not completely happy about it.
    The problem with your solution is that even when there is no output
    the file is created, or the file-stamp changed.
    (Both is not what I want.)

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    Well, that is true with "tee" as well, but you could do something like

    puts $output
    set f [open "textFile.txt" a]
    # fconfigure if needed
    puts $f $output
    close $f

    That would be more expensive, but still less so than spawning off echo,
    and wouldn't create the file unless the write were going to be done.
    --
    columbiaclosings.com
    What's not in Columbia anymore..

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Schelte@21:1/5 to All on Tue Dec 7 20:14:59 2021
    On 07/12/2021 15:39, Ted Nolan <tednolan> wrote:
    I don't think it makes sense to spawn an external command for each update. Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    A more sophisticated way would be to use a stacked channel. Then you can
    do this:

    package require tee
    tee stdout textFile.txt
    puts $output

    And the output will go to both stdout and textFile.txt. If you want to
    return to normal operation, just remove the transformation:

    chan pop stdout

    To accomplish this, put the following code in a file called tee-1.0.tm
    that is somewhere in your tcl::tm::path:

    namespace eval tee {
    variable methods {initialize finalize write}
    namespace ensemble create -command transchan -parameters fd \
    -subcommands $methods
    namespace export tee
    }

    proc tee::tee {chan file} {
    set fd [open $file w]
    chan push $chan [list [namespace which transchan] $fd]
    }

    proc tee::initialize {fd handle mode} {
    variable methods
    return $methods
    }

    proc tee::finalize {fd handle} {
    close $fd
    }

    proc tee::write {fd handle buffer} {
    puts -nonewline $fd $buffer
    return $buffer
    }

    namespace import tee::tee


    Delaying opening the file until something is actually written to stdout
    is left as an exercise for the reader.


    Schelte.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to ted@loft.tnolan.com on Tue Dec 7 21:34:48 2021
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <87wnkgl5th.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update. >>> Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    Yes, I am not completely happy about it.
    The problem with your solution is that even when there is no output
    the file is created, or the file-stamp changed.
    (Both is not what I want.)

    Well, that is true with "tee" as well, but you could do something like

    puts $output
    set f [open "textFile.txt" a]
    # fconfigure if needed
    puts $f $output
    close $f

    That would be more expensive, but still less so than spawning off echo,
    and wouldn't create the file unless the write were going to be done.

    I was already thinking in those lines.
    I created the following script:
    proc testOpen {} {
    set output "Output I want"
    puts ${output}
    set fp [open testFile.txt w]
    puts $fp ${output}
    close $fp
    }
    proc testTee {} {
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt
    }

    for {set i 0} {$i < 10000} {incr i} {
    file delete textFile.txt
    testTee
    }

    This takes about 13.7 user seconds and 17.9 system seconds.

    When I change the testTee to testOpen that becomes 0.4 and 1.5.
    The difference is a lot bigger as I expected.

    An optimisation could be that the first time I need to do output I
    open the file and only close it at the end of the program (when it is
    opened).

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Gerald Lester@21:1/5 to Schelte on Tue Dec 7 15:26:14 2021
    On 12/7/21 1:14 PM, Schelte wrote:
    On 07/12/2021 15:39, Ted Nolan <tednolan> wrote:
    I don't think it makes sense to spawn an external command for each
    update.
    Best to do it all from Tcl.

        set f [open "textFile.txt" w]
        # fconfigure if needed
        ...
        ...
        ...
        puts $output
        puts $f $output
        ...
        ...
        ...
        close $f

    A more sophisticated way would be to use a stacked channel. Then you can
    do this:

        package require tee
        tee stdout textFile.txt
        puts $output

    And the output will go to both stdout and textFile.txt. If you want to
    return to normal operation, just remove the transformation:

        chan pop stdout

    To accomplish this, put the following code in a file called tee-1.0.tm
    that is somewhere in your tcl::tm::path:

        namespace eval tee {
            variable methods {initialize finalize write}
            namespace ensemble create -command transchan -parameters fd \
              -subcommands $methods
            namespace export tee
        }

        proc tee::tee {chan file} {
            set fd [open $file w]
            chan push $chan [list [namespace which transchan] $fd]
        }

        proc tee::initialize {fd handle mode} {
            variable methods
            return $methods
        }

        proc tee::finalize {fd handle} {
           close $fd
        }

        proc tee::write {fd handle buffer} {
            puts -nonewline $fd $buffer
            return $buffer
        }

        namespace import tee::tee


    Delaying opening the file until something is actually written to stdout
    is left as an exercise for the reader.


    Schelte.

    Would make a nice TclLib package!

    (hint, hint)

    --
    +----------------------------------------------------------------------+
    | Gerald W. Lester, President, KNG Consulting LLC |
    | Email: Gerald.Lester@kng-consulting.net | +----------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ted Nolan @21:1/5 to Cecil@decebal.nl on Tue Dec 7 23:02:07 2021
    In article <87sfv4kvbr.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <87wnkgl5th.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update. >>>> Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    Yes, I am not completely happy about it.
    The problem with your solution is that even when there is no output
    the file is created, or the file-stamp changed.
    (Both is not what I want.)

    Well, that is true with "tee" as well, but you could do something like

    puts $output
    set f [open "textFile.txt" a]
    # fconfigure if needed
    puts $f $output
    close $f

    That would be more expensive, but still less so than spawning off echo,
    and wouldn't create the file unless the write were going to be done.

    I was already thinking in those lines.
    I created the following script:
    proc testOpen {} {
    set output "Output I want"
    puts ${output}
    set fp [open testFile.txt w]
    puts $fp ${output}
    close $fp
    }
    proc testTee {} {
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt
    }

    for {set i 0} {$i < 10000} {incr i} {
    file delete textFile.txt
    testTee
    }

    This takes about 13.7 user seconds and 17.9 system seconds.

    When I change the testTee to testOpen that becomes 0.4 and 1.5.
    The difference is a lot bigger as I expected.

    An optimisation could be that the first time I need to do output I
    open the file and only close it at the end of the program (when it is >opened).


    Well, I'm not too surprised as you are creating a lot of processes
    with exec.

    One thing I would note, which doesn't really matter for this test,
    but the two procs don't do exactly the same thing. "testOpen" is going
    to zero out the file each time since it is opened with "w", and proc
    "testTee" is going to append to it.
    --
    columbiaclosings.com
    What's not in Columbia anymore..

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to ted@loft.tnolan.com on Wed Dec 8 01:43:27 2021
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <87sfv4kvbr.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <87wnkgl5th.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    ted@loft.tnolan.com (Ted Nolan <tednolan>) writes:

    In article <871r2omse0.fsf@munus.decebal.nl>,
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Cecil Westerhof <Cecil@decebal.nl> writes:

    In bash I would do something like:
    echo "Output I want" | tee logfile.log

    What is the best way to do this in tcl?

    At the moment I do:
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt


    I don't think it makes sense to spawn an external command for each update.
    Best to do it all from Tcl.

    set f [open "textFile.txt" w]
    # fconfigure if needed
    ...
    ...
    ...
    puts $output
    puts $f $output
    ...
    ...
    ...
    close $f

    Yes, I am not completely happy about it.
    The problem with your solution is that even when there is no output
    the file is created, or the file-stamp changed.
    (Both is not what I want.)

    Well, that is true with "tee" as well, but you could do something like

    puts $output
    set f [open "textFile.txt" a]
    # fconfigure if needed
    puts $f $output
    close $f

    That would be more expensive, but still less so than spawning off echo,
    and wouldn't create the file unless the write were going to be done.

    I was already thinking in those lines.
    I created the following script:
    proc testOpen {} {
    set output "Output I want"
    puts ${output}
    set fp [open testFile.txt w]
    puts $fp ${output}
    close $fp
    }
    proc testTee {} {
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> textFile.txt
    }

    for {set i 0} {$i < 10000} {incr i} {
    file delete textFile.txt
    testTee
    }

    This takes about 13.7 user seconds and 17.9 system seconds.

    When I change the testTee to testOpen that becomes 0.4 and 1.5.
    The difference is a lot bigger as I expected.

    An optimisation could be that the first time I need to do output I
    open the file and only close it at the end of the program (when it is >>opened).


    Well, I'm not too surprised as you are creating a lot of processes
    with exec.

    One thing I would note, which doesn't really matter for this test,
    but the two procs don't do exactly the same thing. "testOpen" is going
    to zero out the file each time since it is opened with "w", and proc "testTee" is going to append to it.

    Oops, that was stupid. :'-( That should have been a.
    Also I used different filenames.
    Cleaned up it becomes:
    proc testOpen {} {
    set output "Output I want"
    puts ${output}
    set fp [open ${::logFile} a]
    puts $fp ${output}
    close $fp
    }
    proc testTee {} {
    set output "Output I want"
    puts ${output}
    exec echo ${output} >> ${::logFile}
    }

    set logFile testFile.txt
    for {set i 0} {$i < 10000} {incr i} {
    file delete ${logFile}
    testTee
    }

    Funny thing. The testOpen becomes faster. For system time it only
    needs 1.0 seconds. Rest of the timings is the same.

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)