Bob Herrmann
Bob Herrmann

Reputation: 9938

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}"

Upvotes: 232

Views: 436517

Answers (8)

starman
starman

Reputation: 1

Complementing emles-kz's answer

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 + '\n').getBytes(encoding))
        inputCatcher.flush()
    }
  
    inputCatcher.write('exit\n'.getBytes(encoding))
    inputCatcher.flush()
    proc.consumeProcessOutput(outputCatcher, errorCatcher)
    proc.waitFor()
    return [new String(outputCatcher.toByteArray(), encoding), new String(errorCatcher.toByteArray(), encoding)]
}

// On Mac or Linux
def out = exec("utf-8", "/", "/bin/sh", ["echo 'test\n'", "ls", "cd usr", "echo", "ls"])

// On Windows
def out = exec("utf-8", "C:", "cmd", ["cd..", "dir"])

println "OUT:\n" + out[0]
println "ERR:\n" + out[1]

jenkins groovy test

Upvotes: 0

舒何伟
舒何伟

Reputation: 51

command = "ls *"

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

but if the command fails, the process will terminate.

Upvotes: -7

emles-kz
emles-kz

Reputation: 99

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]

Upvotes: 8

Aniket Thakur
Aniket Thakur

Reputation: 69035

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).

Upvotes: 29

Bob Herrmann
Bob Herrmann

Reputation: 9938

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

Upvotes: 300

solstice333
solstice333

Reputation: 3659

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.

Upvotes: 41

Joshua
Joshua

Reputation: 26742

"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()

Upvotes: 67

mholm815
mholm815

Reputation: 2069

// 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()
}

Upvotes: 39

Related Questions