214

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}"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bob Herrmann
  • 9,458
  • 10
  • 38
  • 45
  • This [link](http://opensourceforgeeks.blogspot.in/2014/08/executing-shell-commands-in-groovy.html) is useful. Shows how to run shell command with cURL demo. – Aniket Thakur Aug 16 '14 at 06:02

7 Answers7

283

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

Jonas G. Drange
  • 8,749
  • 2
  • 27
  • 38
Bob Herrmann
  • 9,458
  • 10
  • 38
  • 45
  • 18
    In case you also need to set **Environment Variables** to this process, make sure to wrap the command in shell. For example, running a Perforce command with env vars: `envVars = ["P4PORT=p4server:2222", "P4USER=user", "P4PASSWD=pass", "P4CLIENT=p4workspace"]; workDir = new File("path"); cmd = "bash -c \"p4 change -o 1234\""; proc = cmd.execute(envVars, workDir);` – Noam Manos Nov 05 '13 at 09:39
  • 1
    @paul_sns unrelated to the OP question, but I think modern JVMs handle uncontended synchronization just fine. So StringBuffer is unlikely to degrade performance in thread- or stack-confined scenarios . – Pavel Grushetzky Jun 14 '16 at 15:42
  • 5
    The docs say that we should be using waitForProcessOutput() - "To wait for the output to be fully consumed call waitForProcessOutput()". Source: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Process.html#consumeProcessOutput(java.io.OutputStream,%20java.io.OutputStream) – Srikanth Aug 12 '16 at 06:58
  • 4
    @srikanth the waitForProcess() output docs also say "Use this method if you don't care about the standard or error output and just want the process to run silently" - I want the output – Bob Herrmann Aug 12 '16 at 16:32
  • sout and serr might not be available even after the waitForOrKill. Tested using an assert instead of a println. Docs say: "For this, two Threads are started, so this method will return immediately. **The threads will not be join()ed, even if waitFor() is called**. To wait for the output to be fully consumed call waitForProcessOutput()." – solstice333 Feb 10 '17 at 19:48
66

"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()
Alex
  • 8,093
  • 6
  • 49
  • 79
Joshua
  • 26,234
  • 22
  • 77
  • 106
39

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.

solstice333
  • 3,399
  • 1
  • 31
  • 28
38
// 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()
}
mholm815
  • 2,009
  • 19
  • 14
27

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289
8
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]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
emles-kz
  • 99
  • 1
  • 1
  • 5
    I am really annoyed that a person took the time to give an answer and someone just downvoted it for no apparent reason. if this is a community, one should feel obligated to add a comment (unless it's a very obvious reason that any competent programmer would immediately see) explaining the downvote. – Amos Bordowitz Apr 23 '17 at 12:30
  • 10
    @AmosBordowitz Lots of answers get downvotes. It's okay, it's one downvote. That said, it could be because it's code with no word of explanation -- not always well-received. – Chris Baker May 30 '17 at 16:57
  • 3
    @ChrisBaker so why not point it out? You yourself are not positive that this is the reason.. – Amos Bordowitz Jun 05 '17 at 07:49
  • 8
    @AmosBordowitz I am not the official downvote explainer, I cannot tell you why not, and it's understandable that I'm not certain since we're talking about an action taken by another individual. I offered one possibility. Why not explain the downvote, sure, why not explain the code in the answer? At any rate, I'm sure we'll all be okay. – Chris Baker Jun 05 '17 at 13:46
  • 1
    @ChrisBaker we obviously have very different views on what a community should actually look like.. – Amos Bordowitz Jun 06 '17 at 09:32
  • @AmosBordowitz p.s. explaining downvotes is not compulsory for very specific reasons that the community has hashed out over years of discussion, but I guess you know better. – Chris Baker Jun 06 '17 at 13:30
  • 2
    @ChrisBakerI never made any such claim ("but I guess you know better"). It's a decency thing, not a knowledge thing.. – Amos Bordowitz Jun 06 '17 at 13:37
  • @Amos Bordowitz: Does it actually answer the question? Many code dump answers are completely bogus (but no one bothers to check them). The question says *"other than creating a bunch of code to create two threads to read both inputstreams"*. Isn't that what is being implemented here (not a rhetorical question)? – Peter Mortensen Mar 15 '23 at 18:05
  • @PeterMortensen whoa. brought that one back to life haha. Yes, I have since learned these points. I was a newbie then. – Amos Bordowitz Mar 20 '23 at 10:18
-7
command = "ls *"

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

but if the command fails, the process will terminate.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
舒何伟
  • 51
  • 1
  • 2