vho
vho

Reputation: 1141

How to change redirected output?

Is it possible to change the text from redirected output?

For example, consider this code:

set status [catch {eval exec $executableName $options >@ stdout}  errorMessage] 
if { $status != 0 }  {
    return -code error $errorMessage
} 

So if there is a "inside" puts let's suppose: Hello world, is it possible to print it out Hello tcl?

So I would like to do something like this:

  catch {eval exec $executableName $options}  allPuts
  regsub -all "hello world" $errorMessage "hello tcl" allPuts
  puts $allPuts

But in this solution inside puts does not printed on the fly

Upvotes: 0

Views: 249

Answers (1)

Donal Fellows
Donal Fellows

Reputation: 137567

When you use exec $executable {*}$options >@ stdout (a safer version of what you're using that is preferred for reasons that have nothing to do with your question), you are asking for the output of the subprocess to be sent straight to the outer process's standard output with no further processing. If you want to process things more first, you have to either direct the output through a filtering process before directing it to stdout or to bring it into the outer process for processing.

Building a filtering pipeline

In this case, we're using the Unix program sed to do the filtering:

exec $executable {*}$options | sed {s/world/tcl/} >@ stdout

There are many options for doing this sort of thing; any of the many recipes for sed will (probably) work, so long as you remember that you are using Tcl syntax for exec and not shell syntax, so instead of sed 's/world/tcl/' you use sed {s/world/tcl/}.

If you prefer shell syntax, you do this:

set filter "sed 's/world/tcl/'"
exec $executable {*}$options | sh -c $filter >@ stdout

The script in $filter is pure Bourne shell.

Processing inside Tcl

You can also do the transform inside Tcl. To do that on the fly, you need to work asynchronously on an opened pipeline.

# Define this procedure somewhere
proc transformLine {chan transform} {
    if {[gets $chan line] >= 0} {
        puts [string map $transform $line]
    } elseif {[eof $chan]} {
        catch {close $chan} ::doneWithPipe
    }
}

set pipe [open "|$executableName $options"]
fileevent $pipe readable [list transformLine $pipe {"world" "tcl"}]
vwait ::doneWithPipe
return -code error $::doneWithPipe

Note that you have to run the event loop (with vwait) for this to work.

Upvotes: 2

Related Questions