Como faço para que a saída de um comando shell seja executada usando uma variável do Jenkinsfile (groovy)?


213

Eu tenho algo parecido com isso em um Jenkinsfile (Groovy) e quero gravar o stdout e o código de saída em uma variável para poder usar as informações posteriormente.

sh "ls -l"

Como posso fazer isso, especialmente porque parece que você não pode realmente executar nenhum tipo de código groovy dentro do Jenkinsfile?



Respostas:


390

A versão mais recente da shetapa do pipeline permite que você faça o seguinte;

// Git committer email
GIT_COMMIT_EMAIL = sh (
    script: 'git --no-pager show -s --format=\'%ae\'',
    returnStdout: true
).trim()
echo "Git committer email: ${GIT_COMMIT_EMAIL}"

Outro recurso é a returnStatusopção.

// Test commit message for flags
BUILD_FULL = sh (
    script: "git log -1 --pretty=%B | grep '\\[jenkins-full]'",
    returnStatus: true
) == 0
echo "Build full flag: ${BUILD_FULL}"

Essas opções foram adicionadas com base nesse problema.

Veja a documentação oficial para o shcomando.


11
Parece que agora está documentado -> jenkins.io/doc/pipeline/steps/workflow-durable-task-step/…
zot24

Não funciona para mim com o prefixo "vars". Quando eu apenas uso GIT_COMMIT_EMAIL como nome var, sem o prefixo, tudo está bem.
Bastian Voigt


6
Quando estou usando a sintaxe declarativa do jenkinsfile, isso não funciona, a mensagem de erro é:WorkflowScript: 97: Expected a step @ line 97, column 17.
Chave

16
Parece que isso funciona apenas dentro de um scriptbloco de etapas. jenkins.io/doc/book/pipeline/syntax/#declarative-steps
brass monkey


40

resposta rápida é esta:

sh "ls -l > commandResult"
result = readFile('commandResult').trim()

Eu acho que existe uma solicitação de recurso para poder obter o resultado da etapa sh, mas até onde eu sei, atualmente não há outra opção.

EDIT: JENKINS-26133

EDIT2: Não tenho certeza desde qual versão, mas as etapas sh / bat agora podem retornar a saída std, simplesmente:

def output = sh returnStdout: true, script: 'ls -l'

1
Para sua informação, as etapas bat ecoam o comando que está sendo executado, portanto você precisa iniciar os comandos bat com @ para obter apenas a saída (por exemplo, "@dir").
Russell Gallop

21

Se você deseja obter o stdout AND saber se o comando foi bem-sucedido ou não, basta usá returnStdout-lo e envolvê-lo em um manipulador de exceções:

pipeline com script

try {
    // Fails with non-zero exit if dir1 does not exist
    def dir1 = sh(script:'ls -la dir1', returnStdout:true).trim()
} catch (Exception ex) {
    println("Unable to read dir1: ${ex}")
}

saída :

[Pipeline] sh
[Test-Pipeline] Running shell script
+ ls -la dir1
ls: cannot access dir1: No such file or directory
[Pipeline] echo
unable to read dir1: hudson.AbortException: script returned exit code 2

Infelizmente hudson.AbortException está faltando algum método útil para obter esse status de saída, portanto, se o valor real for necessário, você precisará analisá-lo da mensagem (ugh!)

Ao contrário do Javadoc https://javadoc.jenkins-ci.org/hudson/AbortException.html, a construção não falha quando a exceção é capturada. Ele falha quando é não pegou!

Atualização: se você também deseja a saída STDERR do comando shell, o Jenkins infelizmente não suporta adequadamente esse caso de uso comum. Um ingresso para 2017 JENKINS-44930 está preso em um estado de pingue-pongue opinativo, enquanto não progride em direção a uma solução - considere adicionar seu voto positivo a ele.

Quanto a uma solução agora , pode haver algumas abordagens possíveis:

a) Redirecione STDERR para STDOUT 2>&1 - mas você decide analisá-lo da saída principal e não obterá a saída se o comando falhar - porque você está no manipulador de exceções.

b) redirecione o STDERR para um arquivo temporário (cujo nome você preparou anteriormente) 2>filename(mas lembre-se de limpar o arquivo posteriormente) - ie. o código principal se torna:

def stderrfile = 'stderr.out'
try {
    def dir1 = sh(script:"ls -la dir1 2>${stderrfile}", returnStdout:true).trim()
} catch (Exception ex) {
    def errmsg = readFile(stderrfile)
    println("Unable to read dir1: ${ex} - ${errmsg}")
}

c) Vá para o outro lado, defina returnStatus=true, dispense o manipulador de exceções e sempre capture a saída em um arquivo, ou seja:

def outfile = 'stdout.out'
def status = sh(script:"ls -la dir1 >${outfile} 2>&1", returnStatus:true)
def output = readFile(outfile).trim()
if (status == 0) {
    // output is directory listing from stdout
} else {
    // output is error message from stderr
}

Advertência: o código acima é específico para Unix / Linux - o Windows requer comandos shell completamente diferentes.


1
existe uma chance de obter a saída como "ls: não é possível acessar dir1: não existe esse arquivo ou diretório" e não apenas "hudson.AbortException: script retornou o código de saída 2"?
user2988257

Não vejo como isso poderia funcionar. Nos meus testes, o texto de saída nunca é atribuído e isso é de se esperar. Exceção lançada a partir da etapa shell impede o valor de retorno a ser atribuído
Jakub Bochenski

2
returnStatus e returnStdout, infelizmente, não funcionam ao mesmo tempo. Aqui está o bilhete. Por favor, vote em: Issues.jenkins-ci.org/browse/JENKINS-44930 .
Alexander Samoylov 10/10

1
@AlexanderSamoylov Você precisa contornar o problema usando um arquivo como na opção (c) acima. Infelizmente, os autores dessas ferramentas são frequentemente opinativos e não pensam no futuro para outros casos de uso comuns, 'sh' aqui é um caso em questão.
Ed Randall

1
@ Rand Rand, concordo plenamente com você .. É por isso que publiquei esta questão esperando que, devido ao maior número de votos, eles comecem a fazer algo.
Alexander Samoylov

12

este é um exemplo de caso, o que fará sentido, acredito!

node('master'){
    stage('stage1'){
    def commit = sh (returnStdout: true, script: '''echo hi
    echo bye | grep -o "e"
    date
    echo lol''').split()


    echo "${commit[-1]} "

    }
}

Eu não sei o caminho, mas sua resposta me ajudar muito, obrigado :)
shaharnakash

5

Para aqueles que precisam usar a saída em comandos shell subsequentes, em vez de groovy, algo como este exemplo pode ser feito:

    stage('Show Files') {
        environment {
          MY_FILES = sh(script: 'cd mydir && ls -l', returnStdout: true)
        }
        steps {
          sh '''
            echo "$MY_FILES"
          '''
        }
    }

Eu achei os exemplos no code maven bastante úteis.


-6

A maneira mais fácil é usar dessa maneira

my_var=`echo 2` echo $my_var saída: 2

note que não é simples aspas simples é aspas posteriores (`).


Voto a favor, mas eu sugiro que você mostre que eles devem ser agrupados de uma shforma que as pessoas possam achar interessante, especialmente se não estiverem familiarizados com os scripts do bash. Eu apenas tentei no Jenkins, usando em ls -lvez de echo 2e funciona. Eu já havia usado essa abordagem antes, mas estava procurando uma alternativa porque não é muito confiável. Eu tenho a saída de um comando mais complexo capturado em um shell padrão dessa maneira, mas quando portado para o Jenkins, sha variável não possui nada, por algum motivo desconhecido.
Nagev 13/11/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.