Groovy executando comandos de shell

178

O Groovy adiciona o executemétodo Stringpara facilitar a execução de shells;

println "ls".execute().text

mas se ocorrer um erro, não haverá saída resultante. Existe uma maneira fácil de obter o erro padrão e o padrão? (além de criar um monte de código para; criar dois encadeamentos para ler os dois fluxos de entrada e, em seguida, usar um fluxo pai para esperar que eles sejam concluídos e converter as seqüências novamente em texto?)

Seria bom ter algo parecido;

 def x = shellDo("ls /tmp/NoFile")
 println "out: ${x.out} err:${x.err}"
Bob Herrmann
fonte
Este link é útil. Mostra como executar o comando shell com a demonstração cURL.
Aniket Thakur

Respostas:

207

Ok, eu mesmo resolvi;

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

exibe:

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

Bob Herrmann
fonte
13
Caso você também precise definir variáveis ​​de ambiente para esse processo, certifique-se de envolver o comando no shell. Por exemplo, executando um comando Perforce com 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
O @paul_sns não está relacionado à pergunta do OP, mas acho que as JVMs modernas lidam com a sincronização não controlada muito bem. Portanto, é improvável que o StringBuffer diminua o desempenho em cenários limitados por thread ou pilha.
Pavel Grushetzky
3
Os documentos dizem que devemos usar waitForProcessOutput () - "Para aguardar que a saída seja totalmente consumida, chame waitForProcessOutput ()". Fonte: docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/…
Srikanth
4
@srikanth os documentos de saída waitForProcess () também dizem "Use este método se você não se importa com a saída padrão ou com erro e apenas deseja que o processo seja executado silenciosamente" - eu quero a saída
Bob Herrmann
sout e serr podem não estar disponíveis mesmo após o waitForOrKill. Testado usando uma declaração em vez de uma impressão. Os documentos dizem: "Para isso, dois Threads são iniciados, portanto, esse método retornará imediatamente. Os threads não serão ingressados ​​(), mesmo que waitFor () seja chamado . Para aguardar que a saída seja totalmente consumida, chame waitForProcessOutput () . "
Solstice333
49

"ls".execute()retorna um Processobjeto e é por isso que "ls".execute().textfunciona. Você deve conseguir apenas ler o fluxo de erros para determinar se houve algum erro.

Existe um método extra em Processque lhe permitem passar um StringBufferpara recuperar o texto: consumeProcessErrorStream(StringBuffer error).

Exemplo:

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

println proc.text
println b.toString()
Joshua
fonte
Não está trabalhando com Bourn Again Shell roteiro #! / Bin / bash,!
Rashmi Jain
1
Se estiver trabalhando com scripts bash, você provavelmente chamará o bash como parte do comando: "/ 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
fonte
10
+1 Isso mostra a saída de forma incremental como a saída é generated..which é extremamente importante para um processo de longa duração
samarjit samanta
boa parte lá @ mholm815
Jimmy Obonyo Abor
2
Para usar esta solução, emita a seguinte linha: runCommand("echo HELLO WORLD")
Miron V 30/10
@ mholm815 como podemos aprovar os scripts necessários do próprio pipeline?
Ronak Patel 10/10/19
25

Acho isso mais idiomático:

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"

Como outro post menciona, essas chamadas estão bloqueando, mas como queremos trabalhar com a saída, isso pode ser necessário.

solstice333
fonte
24

Para adicionar mais uma informação importante às respostas fornecidas acima -

Para um processo

def proc = command.execute();

sempre tente usar

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

ao invés de

def output = proc.in.text;

capturar as saídas após a execução de comandos no groovy, pois a última é uma chamada de bloqueio ( questão SO por motivo ).

Aniket Thakur
fonte
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]
emles-kz
fonte
3
Estou realmente aborrecido por uma pessoa ter tido tempo para dar uma resposta e alguém simplesmente ter recusado por nenhuma razão aparente. se esta é uma comunidade, deve-se sentir obrigado a adicionar um comentário (a menos que seja uma razão muito óbvia que qualquer programador competente veja imediatamente) explicando o voto negativo.
Amos Bordowitz
5
@AmosBordowitz Muitas respostas são reduzidas. Está tudo bem, é um voto negativo. Dito isto, pode ser porque é um código sem nenhuma palavra de explicação - nem sempre é bem recebido.
Chris Baker
@ChrisBaker então por que não apontar isso? Você mesmo não é positivo que esta é a razão ..
Amos Bordowitz
4
@AmosBordowitz Não sou o explicador oficial de votos negativos, não posso dizer por que não, e é compreensível que não tenha certeza, pois estamos falando de uma ação tomada por outra pessoa. Eu ofereci uma possibilidade. Por que não explicar o voto negativo, claro, por que não explicar o código na resposta? De qualquer forma, tenho certeza que todos ficaremos bem.
Chris Baker
1
@ChrisBakerEu nunca fiz tal afirmação ("mas acho que você sabe melhor"). É uma coisa decência, não é uma coisa conhecimento ..
Amos Bordowitz
-3
command = "ls *"

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

mas se a falha do comando, o processo será encerrado

舒何伟
fonte
De onde shvem?
Styl3r 26/04/19
3
shfaz parte do groovy DSL de Jenkins. Provavelmente não é útil aqui
Gi0rgi0s
4
Jenkins Groovy DSL = Groovy!
Skeeve
como os outros, isso é parte da Jenkins DSL
jonypony3
Esta resposta não é aplicável à pergunta que foi feita.
Brandon