Groovy, das Shell-Befehle ausführt

178

Groovy fügt die executeMethode hinzu String, um das Ausführen von Shells ziemlich einfach zu machen.

println "ls".execute().text

Wenn jedoch ein Fehler auftritt, wird keine Ausgabe ausgegeben. Gibt es eine einfache Möglichkeit, sowohl den Standardfehler als auch den Standard zu ermitteln? (außer eine Menge Code zu erstellen; zwei Threads erstellen, um beide Eingabestreams zu lesen, und dann einen übergeordneten Stream verwenden, um darauf zu warten, dass sie abgeschlossen sind, und dann die Zeichenfolgen wieder in Text konvertieren?)

Es wäre schön, so etwas zu haben;

 def x = shellDo("ls /tmp/NoFile")
 println "out: ${x.out} err:${x.err}"
Bob Herrmann
quelle
Dieser Link ist nützlich. Zeigt, wie der Shell-Befehl mit der cURL-Demo ausgeführt wird.
Aniket Thakur

Antworten:

207

Ok, habe es selbst gelöst;

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

zeigt an:

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

Bob Herrmann
quelle
13
Wenn Sie für diesen Prozess auch Umgebungsvariablen festlegen müssen, stellen Sie sicher, dass der Befehl in die Shell eingeschlossen wird. Beispiel: Ausführen eines Perforce-Befehls mit 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
@paul_sns hat nichts mit der OP-Frage zu tun, aber ich denke, moderne JVMs können mit unkontrollierter Synchronisation problemlos umgehen. Daher ist es unwahrscheinlich, dass StringBuffer die Leistung in Thread- oder Stack-Szenarien beeinträchtigt.
Pavel Grushetzky
3
Die Dokumente sagen, dass wir waitForProcessOutput () verwenden sollten - "Um zu warten, bis die Ausgabe vollständig verbraucht ist, rufen Sie waitForProcessOutput () auf". Quelle: docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/…
Srikanth
4
@srikanth In den Ausgabedokumenten von waitForProcess () heißt es außerdem: "Verwenden Sie diese Methode, wenn Sie sich nicht für die Standard- oder Fehlerausgabe interessieren und nur möchten, dass der Prozess unbeaufsichtigt ausgeführt wird." - Ich möchte die Ausgabe
Bob Herrmann
Sout und Serr sind möglicherweise auch nach dem waitForOrKill nicht verfügbar. Getestet mit einem Assert anstelle eines Println. Die Dokumente sagen: "Dazu werden zwei Threads gestartet, sodass diese Methode sofort zurückgegeben wird. Die Threads werden nicht verknüpft (), auch wenn waitFor () aufgerufen wird . Um zu warten, bis die Ausgabe vollständig verbraucht ist, rufen Sie waitForProcessOutput () auf." . "
Sonnenwende333
48

"ls".execute()Gibt ein ProcessObjekt zurück, weshalb es "ls".execute().textfunktioniert. Sie sollten in der Lage sein, nur den Fehlerstrom zu lesen, um festzustellen, ob Fehler aufgetreten sind.

Es gibt eine zusätzliche Methode Process, mit der Sie eine übergeben können StringBuffer, um den Text abzurufen : consumeProcessErrorStream(StringBuffer error).

Beispiel:

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

println proc.text
println b.toString()
Joshua
quelle
Es funktioniert nicht mit Bourn Again Shell-Skript! # / Bin / bash,
Rashmi Jain
1
Wenn Sie mit Bash-Skripten arbeiten, rufen Sie wahrscheinlich Bash als Teil des Befehls auf: "/ 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()
}
mholm815
quelle
10
+1 Dies zeigt die Ausgabe inkrementell, wenn die Ausgabe generiert wird. Dies ist äußerst wichtig für einen lang laufenden Prozess
Samarjit Samanta
großer Anteil dort @ mholm815
Jimmy Obonyo Abor
2
Um diese Lösung zu verwenden, geben Sie die folgende Zeile ein:runCommand("echo HELLO WORLD")
Miron V
@ mholm815 Wie können wir die erforderlichen Skripte aus der Pipeline selbst genehmigen?
Ronak Patel
25

Ich finde das idiomatischer:

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"

Wie in einem anderen Beitrag erwähnt, blockieren diese Anrufe, aber da wir mit der Ausgabe arbeiten möchten, kann dies erforderlich sein.

Sonnenwende333
quelle
24

Um eine weitere wichtige Information zu den oben angegebenen Antworten hinzuzufügen -

Für einen Prozess

def proc = command.execute();

versuche immer zu benutzen

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

eher, als

def output = proc.in.text;

Das Erfassen der Ausgänge nach dem Ausführen von Befehlen in Groovy, da letzterer ein blockierender Aufruf ist ( SO-Frage aus gutem Grund ).

Aniket Thakur
quelle
5
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]
emles-kz
quelle
3
Ich bin wirklich verärgert, dass sich eine Person die Zeit genommen hat, eine Antwort zu geben, und jemand hat sie ohne ersichtlichen Grund einfach abgelehnt. Wenn dies eine Community ist, sollte man sich verpflichtet fühlen, einen Kommentar hinzuzufügen (es sei denn, es ist ein sehr offensichtlicher Grund, den ein kompetenter Programmierer sofort sehen würde), der die Ablehnung erklärt.
Amos Bordowitz
4
@AmosBordowitz Viele Antworten werden abgelehnt. Es ist okay, es ist eine Ablehnung. Das heißt, es könnte sein, dass es sich um Code ohne Erklärungswort handelt - nicht immer gut aufgenommen.
Chris Baker
@ ChrisBaker also warum nicht darauf hinweisen? Sie selbst sind sich nicht
sicher,
4
@AmosBordowitz Ich bin nicht der offizielle Downvote-Erklärer, ich kann Ihnen nicht sagen, warum nicht, und es ist verständlich, dass ich nicht sicher bin, da es sich um eine Aktion handelt, die von einer anderen Person ergriffen wurde. Ich habe eine Möglichkeit angeboten. Warum nicht die Ablehnung erklären, klar, warum nicht den Code in der Antwort erklären? Ich bin mir jedenfalls sicher, dass es uns allen gut gehen wird.
Chris Baker
1
@ ChrisBakerIch habe nie einen solchen Anspruch erhoben ("aber ich denke, du weißt es besser"). Es ist eine Anstandssache, keine Wissenssache.
Amos Bordowitz
-3
command = "ls *"

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

Wenn der Befehl fehlschlägt, wird der Prozess beendet

舒何伟
quelle
Woher kommt shdas?
styl3r
3
shist Teil des Jenkins groovigen DSL. Wahrscheinlich nicht nützlich hier
Gi0rgi0s
4
Jenkins Groovy DSL! = Groovy
Skeeve
Wie andere gesagt haben, ist dies Teil der Jenkins DSL
Jonypony3
Diese Antwort gilt nicht für die gestellte Frage.
Brandon