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