Groovy Executing Shell Commands

Groovy Executing Shell Commands

Groovy adds the execute method to String to make executing shells fairly easy;

println "ls".execute().text

but if an error happens, then there is no resulting output. Is there an easy way to get both the standard error and standard output? (other than creating a bunch of code to create two threads to read both inputstreams, using a parent stream to wait for them to complete, and then convert the strings back to text?)

It would be nice to have something like;

 def x = shellDo("ls /tmp/NoFile")
 println "out: ${x.out} err:${x.err}"
1

7 Answers

Ok, solved it myself;

def sout = new StringBuilder(), serr = new StringBuilder()
def proc = 'ls /badDir'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout\nerr> $serr"

displays:

out> err> ls: cannot access /badDir: No such file or directory

5

"ls".execute() returns a Process object which is why "ls".execute().text works. You should be able to just read the error stream to determine if there were any errors.

There is a extra method on Process that allow you to pass a StringBuffer to retrieve the text: consumeProcessErrorStream(StringBuffer error).

Example:

def proc = "ls".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)

println proc.text
println b.toString()
2

I find this more idiomatic:

def proc = "ls foo.txt doesnotexist.txt".execute()
assert proc.in.text == "foo.txt\n"
assert proc.err.text == "ls: doesnotexist.txt: No such file or directory\n"

As another post mentions, these are blocking calls, but since we want to work with the output, this may be necessary.

// a wrapper closure around executing a string                                  
// can take either a string or a list of strings (for arguments with spaces)    
// prints all output, complains and halts on error                              
def runCommand = { strList ->
  assert ( strList instanceof String ||
           ( strList instanceof List && strList.each{ it instanceof String } ) \
)
  def proc = strList.execute()
  proc.in.eachLine { line -> println line }
  proc.out.close()
  proc.waitFor()

  print "[INFO] ( "
  if(strList instanceof List) {
    strList.each { print "${it} " }
  } else {
    print strList
  }
  println " )"

  if (proc.exitValue()) {
    println "gave the following error: "
    println "[ERROR] ${proc.getErrorStream()}"
  }
  assert !proc.exitValue()
}
4

To add one more important piece of information to the previous answers:

For a process

def proc = command.execute();

always try to use

def outputStream = new StringBuffer();
proc.waitForProcessOutput(outputStream, System.err)
//proc.waitForProcessOutput(System.out, System.err)

rather than

def output = proc.in.text;

to capture the outputs after executing commands in Groovy as the latter is a blocking call (SO question for reason).

def exec = { encoding, execPath, execStr, execCommands ->

    def outputCatcher = new ByteArrayOutputStream()
    def errorCatcher = new ByteArrayOutputStream()

    def proc = execStr.execute(null, new File(execPath))
    def inputCatcher = proc.outputStream

    execCommands.each { cm ->
        inputCatcher.write(cm.getBytes(encoding))
        inputCatcher.flush()
    }

    proc.consumeProcessOutput(outputCatcher, errorCatcher)
    proc.waitFor()

    return [new String(outputCatcher.toByteArray(), encoding), new String(errorCatcher.toByteArray(), encoding)]
}

def out = exec("cp866", "C:\\Test", "cmd", ["cd..\n", "dir\n", "exit\n"])

println "OUT:\n" + out[0]
println "ERR:\n" + out[1]
9
command = "ls *"

def execute_state=sh(returnStdout: true, script: command)

but if the command fails, the process will terminate.

6

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Chloe Bennett
Author

Chloe Bennett

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.