Groovy実行シェルコマンド


178

Groovyはexecuteメソッドを追加しますStringシェルの実行をかなり簡単するためます。

println "ls".execute().text

エラーが発生した場合、結果は出力されません。 標準エラーと標準出力の両方を取得する簡単な方法はありますか? (コードの束を作成する以外に、2つのスレッドを作成して両方の入力ストリームを読み取り、次に親ストリームを使用してそれらが完了するのを待ってから文字列をテキストに変換しますか?)

次のようなものがあると便利です。

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

このリンクは役に立ちます。cURLデモでシェルコマンドを実行する方法を示します。
Aniket Thakur 2014

回答:


207

OK、自分で解決しました。

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

表示:

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


13
このプロセスに環境変数も設定する必要がある場合は、必ずコマンドをシェルでラップしてください。たとえば、ENVでのPERFORCEコマンドを実行すると、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);
ノーム・マノス

@paul_snsはOPの質問とは無関係ですが、最新のJVMは競合しない同期をうまく処理すると思います。そのため、StringBufferは、スレッドまたはスタックに限定されたシナリオでパフォーマンスを低下させることはほとんどありません。
Pavel Grushetzky

3
ドキュメントでは、waitForProcessOutput()を使用する必要があると述べています-「出力が完全に消費されるのを待つには、waitForProcessOutput()を呼び出します」。出典:docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/...
スリカンス

4
@srikanthのwaitForProcess()出力ドキュメントには、「標準出力やエラー出力を気にせず、プロセスをサイレントに実行したいだけの場合にこのメソッドを使用する」とあります
Bob Herrmann

soutおよびserrは、waitForOrKillの後でも使用できない場合があります。printlnの代わりにassertを使用してテストされました。ドキュメントでは、「このため、2つのスレッドが開始されるため、このメソッドはすぐに戻ります。waitFor()が呼び出されても、スレッドはjoin()されません。出力が完全に消費されるのを待つには、waitForProcessOutput()を呼び出します。 」
solstice333

49

"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シェルスクリプトでは動作しません!#/ bin / bash
Rashmi Jain

1
bashスクリプトを使用する場合は、おそらくコマンドの一部としてbashを呼び出します。 "/ bin / bash script" .execute()
Niels Bech Nielsen

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

10
+1これは、出力が生成されるにつれて出力を段階的に示します。これは、長時間実行プロセスにとって非常に重要です
samarjit samanta

素晴らしい共有@ mholm815
ジミーオボニョアボー

2
:このソリューションを使用するには、次の行を発行runCommand("echo HELLO WORLD")
Miron氏V

@ mholm815パイプライン自体から必要なスクリプトをどのように承認できますか?
ロナックパテル

25

私はこれをもっと慣用的にしています:

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"

別の投稿で言及されているように、これらは呼び出しをブロックしていますが、出力を操作するため、これが必要になる場合があります。


24

上記の回答に重要な情報をもう1つ追加するには-

プロセスについて

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の質問です)。


6
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]

3
人が時間をかけて答えを出し、誰かが明確な理由なしに反対票を投じたのは本当にイライラしています。これがコミュニティーである場合、ダウンボットを説明するコメントを追加する義務があると感じるべきです(有能なプログラマーがすぐにわかる非常に明白な理由でない限り)。
Amos Bordowitz 2017

5
@AmosBordowitz多くの回答が反対票を得ます。それは大丈夫です、それは1つの反対票です。とは言っても、説明のないコードであるためか、常に好評であるとは限りません。
Chris Baker

@ChrisBakerそれでは、なぜそれを指摘しないのですか?あなた自身は、これが理由であることを肯定的ではありません...
アモスBordowitz

4
@AmosBordowitz私は公式の反対投票の説明者ではありません。理由を説明することはできません。また、別の個人が行ったアクションについて話しているので、はっきりしません。1つの可能性を提供しました。なぜ反対票を説明しないのですか?確かに、答えのコードを説明しないのですか?とにかく、大丈夫だと思います。
クリスベイカー

1
@ChrisBaker私はそのような主張をしたことはありません(「私はあなたがもっと知っていると思います」)。それは良識のものではなく、知識のことだ...
アモスBordowitz

-3
command = "ls *"

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

コマンドが失敗した場合、プロセスは終了します


どこshから来たの?
styl3r

3
shジェンキンスのグルーヴィーなDSLの一部です。おそらくここでは役に立たない
Gi0rgi0s

4
Jenkins Groovy DSL!= Groovy
Skeeve 2018年

他の人が述べたように、これはジェンキンスDSLの一部です
jonypony3

この回答は、尋ねられた質問には適用されません。
Brandon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.