Groovy esegue i comandi della shell


178

Groovy aggiunge il executemetodo per Stringrendere l'esecuzione delle shell abbastanza semplice;

println "ls".execute().text

ma se si verifica un errore, non viene generato alcun risultato. C'è un modo semplice per ottenere sia l'errore standard sia lo standard? (diverso dalla creazione di un gruppo di codice in; creare due thread per leggere entrambi i flussi di input, quindi utilizzare uno stream padre per attendere il loro completamento, quindi riconvertire le stringhe in testo?)

Sarebbe bello avere qualcosa di simile;

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

Questo link è utile Mostra come eseguire il comando shell con demo cURL.
Aniket Thakur,

Risposte:


207

Ok, l'ho risolto da solo;

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

display:

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


13
Nel caso in cui sia necessario impostare anche Variabili d'ambiente su questo processo, assicurarsi di avvolgere il comando nella shell. Ad esempio, eseguendo un comando Perforce con 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 non è correlato alla domanda di OP, ma penso che le moderne JVM gestiscano la sincronizzazione non corretta. Pertanto, è improbabile che StringBuffer degradi le prestazioni in scenari limitati da thread o stack.
Pavel Grushetzky,

3
I documenti affermano che dovremmo usare waitForProcessOutput () - "Per attendere che l'output sia completamente consumato chiama waitForProcessOutput ()". Fonte: docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/…
Srikanth,

4
@srikanth the waitForProcess () i documenti di output dicono anche "Usa questo metodo se non ti interessa l'output standard o di errore e vuoi solo che il processo venga eseguito in modo silenzioso" - Voglio l'output
Bob Herrmann

sout e serr potrebbero non essere disponibili anche dopo waitForOrKill. Testato usando un'asserzione anziché una println. I documenti dicono: "Per questo, vengono avviati due thread, quindi questo metodo tornerà immediatamente. I thread non saranno join () ed, anche se viene chiamato waitFor () . Per attendere che l'output sia completamente consumato chiama waitForProcessOutput () ".
solstice333,

49

"ls".execute()restituisce un Processoggetto, motivo per cui "ls".execute().textfunziona. Dovresti essere in grado di leggere il flusso di errori per determinare se si sono verificati errori.

C'è un metodo in più su Processche consentono di passare una StringBufferper recuperare il testo: consumeProcessErrorStream(StringBuffer error).

Esempio:

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

println proc.text
println b.toString()

Non funziona con lo script di Bourn Again Shell! # / Bin / bash,
Rashmi Jain,

1
Se lavori con script bash, probabilmente invochi bash come parte del comando: "/ bin / bash script" .execute ()
Niels Bech Nielsen il

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 Questo mostra l'output in modo incrementale man mano che l'output viene generato ... che è estremamente importante per un processo di lunga durata
samarjit samanta

grande condivisione lì @ mholm815
Jimmy Obonyo Abor

2
Per utilizzare questa soluzione, emettere la seguente riga:runCommand("echo HELLO WORLD")
Miron V

@ mholm815 come possiamo approvare gli script richiesti dalla pipeline stessa?
Ronak Patel,

25

Lo trovo più idiomatico:

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"

Come menziona un altro post, si tratta di bloccare le chiamate, ma poiché vogliamo lavorare con l'output, questo potrebbe essere necessario.


24

Per aggiungere un'altra informazione importante alle risposte fornite sopra -

Per un processo

def proc = command.execute();

cerca sempre di usare

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

piuttosto che

def output = proc.in.text;

catturare gli output dopo aver eseguito i comandi in groovy poiché quest'ultimo è una chiamata bloccante ( domanda SO per motivo ).


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
Sono davvero infastidito dal fatto che una persona abbia avuto il tempo di dare una risposta e qualcuno l'ha appena votata senza motivo apparente. se questa è una comunità, ci si dovrebbe sentire obbligati ad aggiungere un commento (a meno che non sia una ragione molto ovvia che qualsiasi programmatore competente vedrebbe immediatamente) spiegando il downvote.
Amos Bordowitz,

6
@AmosBordowitz Molte risposte ottengono voti bassi. Va bene, è un voto negativo. Detto questo, potrebbe essere perché è un codice senza una parola di spiegazione - non sempre ben accolto.
Chris Baker,

@ChrisBaker, quindi perché non segnalarlo? Tu stesso non sei
sicuro

5
@AmosBordowitz Non sono lo spiegatore ufficiale del downvote, non posso dirti perché no, ed è comprensibile che non ne sia certo poiché stiamo parlando di un'azione intrapresa da un altro individuo. Ho offerto una possibilità. Perché non spiegare il downvote, certo, perché non spiegare il codice nella risposta? Ad ogni modo, sono sicuro che staremo tutti bene.
Chris Baker,

1
@ChrisBaker Non ho mai fatto una simile affermazione ("ma suppongo tu lo sappia meglio"). È una cosa
decente

-3
command = "ls *"

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

ma se il comando fallisce il processo termina


Da dove shviene
styl3r

3
shfa parte del groove DSL di Jenkins. Probabilmente non utile qui
Gi0rgi0s

4
Jenkins Groovy DSL! = Groovy
Skeeve,

come altri hanno affermato, questo fa parte del DSL Jenkins
jonypony3,

Questa risposta non è applicabile alla domanda che è stata posta.
Brandon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.