从 groovy 脚本调用的外部进程处理多行输出的最佳方法?

Best way to work on multi-line output from external process called from groovy script?

我用这个方法:

def runProcess(List cmd) {
  def process = cmd.execute()
  def output = new StringWriter(), error = new StringWriter()
  process.waitForProcessOutput(output, error)
  def exitCode = process.exitValue()
  if (exitCode) {
    throw new Exception("Error: $error with code: $exitCode")
  }
  return output.toString().split()
}

到 运行 可以 return 一行或多行输出的外部进程。

有时我需要检查每一行,如果找到则 return 匹配。首先,我尝试使用 eachLine 闭包,但发现我无法从 (Can you break from a Groovy "each" closure?):

return
def sample() {
  String tagName = ""
  def tags = runProcess(["git", "tag"])
  tags.eachLine { tag -> 
    println "tag $tag"
    if(tag = "mytag") {
      tagName = tag
      // Cannot return from a eachLine closure :-(
      return tag
    }
  }
  return tagName
}

上面的方法可行,但如果我有 1000 行,它将遍历所有行 - 因为 return 语句被忽略了。

我现在正在尝试经典的 for 循环:

def sample() {

  def tags = runProcess(["git", "tag"])
  println "tags.getClass() "  + tags.getClass() // this is java.lang.String
  String[] tagsArr = tags.split("\n");
  println "tags.getClass() "  + tagsArr.getClass() // this is [Ljava.lang.String
     
  if (tagsArr != null && tagsArr.length > 0) { // does NOT = true when tagsArr is empty :-(
     for (String tag : tagsArr) {
        def desc = shellCommand(["git", "describe","--tags","$tag"])
        if(desc.trim() == "mytag") {
          println "Found tag: $tag at HEAD"
          return tag
        }
     }
  }
}

这很漂亮 verbose/ugly 并且当 tagsArremptynot 是否工作(仍在调查这个和感谢任何输入!)。

关于如何更好地处理调用外部进程的多行输出有什么建议吗?

还有上面的这个:

return output.toString().split()

好像不太对...

我也看过:

http://konstructcomputers.blogspot.com/2013/12/groovy-line-by-line-process-output.html

看起来相当verbose/extensive。我希望 groovy 提供一些最小的方法来避免这种“低级”样板代码。

旁注。有趣的是这个页面:

http://docs.groovy-lang.org/latest/html/documentation/working-with-io.html

没有提到 waitForProcessOutput 我认为这是从外部进程获取输出的最可靠方法。

更新示例(使用 readLines 和查找)

下面是一个基于以下建议的更新示例。我修改了原始示例以使其更清楚。

预期结果=注释

$ git for-each-ref refs/tags
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/001
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/002
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/003
706a21e04441c43e2b0372bd8607be74c0377690 tag    refs/tags/annotated
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/lightweight

使用 find() 和 split()(有效)

def runProcess(List cmd) {
  def process = cmd.execute()
  def outputWriter = new StringWriter(), errorWriter = new StringWriter()
  process.waitForProcessOutput(outputWriter, errorWriter)
  String output = outputWriter.toString()
  String error = errorWriter.toString()
  int exitCode = process.exitValue()
  if (exitCode) {
    throw new Exception("Error: $error exit code: $exitCode")
  }
  // Notice split()
  return output.split()
}

def sample() {
  def tags = runProcess(["git", "tag","--points-at","HEAD"])
  def result = tags.find{tag -> 
    def tagType = runProcess(["git", "cat-file","-t","$tag"])
    if(tagType[0] == "tag") {
      return tag
    }
  }
  return result
}

assert(result == "annotated")

readLines() 和 find()(也有效)

def runProcess(List cmd) {
  def process = cmd.execute()
  def outputWriter = new StringWriter(), errorWriter = new StringWriter()
  process.waitForProcessOutput(outputWriter, errorWriter)
  String output = outputWriter.toString()
  String error = errorWriter.toString()
  int exitCode = process.exitValue()
  if (exitCode) {
    throw new Exception("Error: $error exit code: $exitCode")
  }
  // Notice NO split()
  return output
}

def sample() {
  def tags = runProcess(["git", "tag","--points-at","HEAD"])

  def result = tags.readLines().find{ tag ->
    def tagType = runProcess(["git", "cat-file","-t","$tag"])
    if(tagType.trim()  == "tag") {
      return tag
    }
  }
  return result
}

assert(result == "annotated")
String sample() {
  def tags = runProcess(["git", "tag"]) // expected multiline string to be returned
  tags.readLines().find{tag->
    def desc = ...
    return desc=="mytag" // will break on first true returned
  }
}

这是您可以在任何在线控制台中 运行 的测试脚本:

def tags = '''
aaa
bbb
ccc
'''

def found = tags.readLines().find{tag->
    return "BBB" ==  tag.toUpperCase()
}

println "found: ${found}"

https://groovyconsole.appspot.com/edit/5201583847505920?execute

另一个基于更新问题的例子:

def output = '''
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/001
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/002
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/003
706a21e04441c43e2b0372bd8607be74c0377690 tag    refs/tags/annotated
67a27fec636a12346f391c67be01a0c8c0e1b7b8 commit refs/tags/lightweight
'''

def tagname = output
                .readLines()
                .findAll()             //keep only non-empty lines
                .collect{it.split()}   //split each line by spaces
                .findAll{it[1]=='tag'} //find all items with 'tag'
                .findResult{tag->
                    def result = true        //evaluate something here to find the one
                    if(result)return tag[2]  //search done - return the value
                    else return null         //continue searching
                }
assert tagname=='refs/tags/annotated'