Version 1 of bexec

Updated 2014-05-11 23:45:32 by pooryorick

How to do binary-safe "exec":

proc bexec {command input} {
    # Execute shell "command", send "input" to stdin, return stdout.
    # Ignores stderr (but "2>@1" can be part of "command").
    # Supports binary input and output. e.g.:
    #     set flac_data [bexec {flac -} $wav_data]

    # Run "command" in background...
    set f [open |$command {RDWR BINARY}]
    fconfigure $f -blocking 0

    # Connect read function to collect "command" output...
    set ::bexec_done.$f 0
    set ::bexec_output.$f {}
    fileevent $f readable [list bexec_read $f]

    # Send "input" to command...
    puts -nonewline $f $input
    close $f write

    # Wait for read function to signal "done"...
    vwait ::bexec_done.$f

    # Retrieve output...
    set result [set ::bexec_output.$f]
    unset ::bexec_output.$f
    unset ::bexec_done.$f

    fconfigure $f -blocking 1
    try {
        close $f
    } trap {CHILDSTATUS} {options info} {
        dict set info -errorinfo $result
        return -options $info $result
    }

    return $result
}


proc bexec_read {f} {
    # Accumulate output in ::bexec_output.$f.

    append ::bexec_output.$f [read $f]
    if {[eof $f]} {
        fileevent $f readable {}
        set ::bexec_done.$f 1
    }
}

PYK 2014-05-11: The code above does not need to go to the effort of setting the channel to non-blocking , as Tcl will manage the buffer behind the scenes . Forget about setting the channel to non-blocking , dispense with the vwait , send the desired data into the channel , close the write side of the channel , and then read the output . For code that does need to use non-blocking channels , it's generally best not to interfere with the event loop by using vwait as the code above does . Instead , consider structuring the code such that it works in an event-oriented manner . See also Example of reading and writing to a piped command , which provides a template for conducting an interactive conversation with another process .