Methods Methods - 4 years ago 205
Groovy Question

Groovy exec buffering output - not printing until batch file completes

When executing a bat file from Groovy, the output of this is not printed from the Groovy script until the bat file is complete. To compare, I tested the same exact bat file from C# and Perl. These both print the output of the bat file as it's being written to STDOUT.

def cmd = "batfile.bat param1"
println cmd.execute().text()


Is there a way to tell Groovy to read the stream and print immediately?

Thank you for the response! I addition to note is when using the recommendation the exec did not wait for the process to complete, which we desire in this case, so adding process.waitFor() accomplished this. Working code example below. (Note test.bat is anything you like, such as: sleep 5)

import groovy.time.*

def times = [:]
def progStartTime = new Date()
String[] caches = ["cache1", "cache2", "cache3"]
def cmd
def batFile = "test.bat "

println new Date()
for (String item : caches) {
def timeStart = new Date()
cmd = [batFile, item]
//println cmd.execute().text
def process = cmd.execute()
process.consumeProcessOutput(System.out, System.err)
process.waitFor()
def timeStop = new Date()
TimeDuration duration = TimeCategory.minus(timeStop, timeStart)
println "cache: " + item + " : " + duration
times.put(item,duration)
}

times.each{ k, v -> println "cache: ${k}, took: ${v}" }

def progStopTime = new Date()
TimeDuration duration = TimeCategory.minus(progStopTime, progStartTime)
println "Total Program duration: " + duration
println new Date()

Answer Source

First of all I believe it should read:

cmd.execute().text

without parenthesis so that we call the groovy Process.getText() method. However that will not solve your problem as the getText() method waits for process completion before returning.

If you don't need control of the output but just want it directly on standard out and standard err, you can use the groovy Process.consumeProcessOutput() method:

def process = "batfile.bat param1".execute()
process.consumeProcessOutput(System.out, System.err) 

This will output the process out and err stream output directly on the system out and err streams as the output becomes available.

If you need processing or control, something like the following should solve your problem:

def process = "batfile.bat param1".execute()
process.in.withReader { r -> 
  r.eachLine { line -> 
    // some token processing
    println "batfile output> ${line.toUpperCase()}"
  }
}

also parameters with spaces tend to cause havoc so I have found it is often safer to use the groovy List.execute() form instead as in:

def process = ["batfile.bat", "param1"].execute()

which does the same thing but keeps parameter integrity with regards to spaces.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download