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?

sorin
fonte
2
Duplicar de stackoverflow.com/questions/36507410/… .
precisa saber é o seguinte

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.

G. Roggemans
fonte
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
Por favor, ajude-me para stackoverflow.com/questions/40946697/…
Jitesh Sojitra
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
51

A versão atual do Pipeline suporta nativamente returnStdoute returnStatus, o que torna possível obter saída ou status de sh/ batsteps.

Um exemplo:

def ret = sh(script: 'uname', returnStdout: true)
println ret

Uma documentação oficial .

luka5z
fonte
Alguém pode me ajudar com stackoverflow.com/questions/40946697/… ? Desde já, obrigado!
Jitesh Sojitra # 3/16
2
As declarações devem ser agrupadas em uma script { }etapa.
X-yuri
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'
vehovmar
fonte
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.

Ed Randall
fonte
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]} "

    }
}
Bibek Mantree
fonte
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.

Nagev
fonte
-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 (`).

Ajay Gadhavana
fonte
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