Groovy 执行 shell 命令

Posted

技术标签:

【中文标题】Groovy 执行 shell 命令【英文标题】:Groovy executing shell commands 【发布时间】:2010-09-14 14:54:23 【问题描述】:

Groovy 将 execute 方法添加到 String 以使执行 shell 相当容易;

println "ls".execute().text

但如果发生错误,则没有结果输出。 有没有一种简单的方法来获取标准错误和标准输出?(除了创建一堆代码;创建两个线程来读取两个输入流,然后使用父流等待它们完成然后将字符串转换回文本?)

如果有类似的东西就好了;

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

【问题讨论】:

这个link 很有用。展示如何使用 cURL 演示运行 shell 命令。 【参考方案1】:

好的,我自己解决了;

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

显示:

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

【讨论】:

如果您还需要为此进程设置 环境变量,请确保将命令包装在 shell 中。例如,使用环境变量运行 Perforce 命令: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); @paul_sns 与 OP 问题无关,但我认为现代 JVM 可以很好地处理非竞争同步。因此 StringBuffer 在线程或堆栈受限的场景中不太可能降低性能。 文档说我们应该使用 waitForProcessOutput() - “等待输出完全消耗调用 waitForProcessOutput()”。来源:docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/… @srikanth waitForProcess() 输出文档还说“如果您不关心标准或错误输出并且只想让进程静默运行,请使用此方法” - 我想要输出跨度> sout 和 serr 即使在 waitForOrKill 之后也可能不可用。使用断言而不是 println 进行测试。文档说:“为此,启动了两个线程,因此此方法将立即返回。即使调用了 waitFor(),线程也不会被 join()。等待输出被完全消耗调用waitForProcessOutput()。”【参考方案2】:
command = "ls *"

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

但如果命令失败,进程将终止

【讨论】:

sh 来自哪里? sh 是 Jenkins groovy DSL 的一部分。在这里可能没用 Jenkins Groovy DSL != Groovy 正如其他人所说,这是 Jenkins DSL 的一部分 此答案不适用于所提出的问题。【参考方案3】:

我觉得这更惯用:

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"

正如另一篇文章提到的,这些是阻塞调用,但由于我们想要处理输出,这可能是必要的。

【讨论】:

【参考方案4】:
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]

【讨论】:

我真的很生气,有人花时间给出答案,而有人无缘无故地否决了它。如果这是一个社区,则应该感到有义务添加评论(除非这是任何有能力的程序员都会立即看到的非常明显的原因)解释反对票。 @AmosBordowitz 很多答案都被否决了。没关系,这是一票否决。也就是说,这可能是因为它的代码没有任何解释——并不总是很受欢迎。 @ChrisBaker 那么为什么不指出来呢?你自己并不肯定这就是原因.. @AmosBordowitz 我不是官方的否决解释者,我不能告诉你为什么不这样做,我不确定是可以理解的,因为我们谈论的是另一个人采取的行动。我提供了一种可能性。为什么不解释反对票,当然,为什么不解释答案中的代码?无论如何,我相信我们都会好起来的。 @ChrisBaker我从未提出过任何此类声明(“但我猜你知道得更好”)。这是体面的事情,而不是知识的事情..【参考方案5】:

"ls".execute() 返回一个Process 对象,这就是"ls".execute().text 起作用的原因。您应该能够只读取错误流以确定是否有任何错误。

Process 上有一个额外的方法,允许您传递 StringBuffer 来检索文本:consumeProcessErrorStream(StringBuffer error)

例子:

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

println proc.text
println b.toString()

【讨论】:

它不适用于 Bourn Again Shell 脚本!#/bin/bash, 如果使用 bash 脚本,您可能会调用 bash 作为命令的一部分:“/bin/bash script”.execute()【参考方案6】:

在上面提供的答案中添加一个更重要的信息 -

对于一个进程

def proc = command.execute();

总是尝试使用

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

而不是

def output = proc.in.text;

在 groovy 中执行命令后捕获输出,因为后者是阻塞调用 (SO question for reason)。

【讨论】:

【参考方案7】:
// 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()

【讨论】:

+1 这会随着输出的生成递增地显示输出。这对于长时间运行的进程非常重要 要使用此解决方案,请发出以下行:runCommand("echo HELLO WORLD") @mholm815 我们如何从管道本身批准所需的脚本?

以上是关于Groovy 执行 shell 命令的主要内容,如果未能解决你的问题,请参考以下文章

从 GroovyConsole 执行 shell 命令时出错

jenkins执行groovy 脚本报错

Groovy Jenkins 脚本中的 sh 命令

从Java执行Groovy

通过Jenkins执行Groovy命令:“find:缺少`-exec'的参数”

Android关于Groovy脚本执行ADB命令