¿Cómo hago para que se ejecute la salida de un comando de shell utilizando una variable de Jenkinsfile (groovy)?

213

Tengo algo como esto en un Jenkinsfile (Groovy) y quiero grabar la stdout y el código de salida en una variable para poder usar la información más adelante.

sh "ls -l"

¿Cómo puedo hacer esto, especialmente porque parece que realmente no puedes ejecutar ningún tipo de código maravilloso dentro del Jenkinsfile?

Sorin
fuente

Respuestas:

393

La última versión del shpaso de canalización le permite hacer lo siguiente;

// 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}"

Otra característica es la returnStatusopción.

// 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}"

Estas opciones se agregaron en función de este problema.

Consulte la documentación oficial para el shcomando.

G. Roggemans
fuente
11
Parece que ahora está documentado -> jenkins.io/doc/pipeline/steps/workflow-durable-task-step/…
zot24
Sin embargo, no funciona para mí con el prefijo "vars". Cuando solo uso GIT_COMMIT_EMAIL como nombre de var sin el prefijo, todo está bien.
Bastian Voigt
Por favor
ayúdenme
77
Cuando uso la sintaxis declarativa jenkinsfile, esto no funciona, el mensaje de error es:WorkflowScript: 97: Expected a step @ line 97, column 17.
Wrench
17
Parece que esto funciona solo dentro de un scriptbloque de pasos. jenkins.io/doc/book/pipeline/syntax/#declarative-steps
brass monkey
51

La versión actual de Pipeline admite de forma nativa returnStdouty returnStatus, lo que permite obtener resultados o estados de sh/ batsteps.

Un ejemplo:

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

Una documentación oficial .

luka5z
fuente
¿Alguien me puede ayudar para stackoverflow.com/questions/40946697/… ? ¡Gracias por adelantado!
Jitesh Sojitra
3
Las declaraciones deben envolverse en un script { }paso.
x-yuri
40

La respuesta rápida es esta:

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

Creo que existe una solicitud de función para poder obtener el resultado del paso sh, pero que yo sepa, actualmente no hay otra opción.

EDITAR: JENKINS-26133

EDIT2: No estoy seguro desde qué versión, pero los pasos sh / bat ahora pueden devolver la salida estándar, simplemente:

def output = sh returnStdout: true, script: 'ls -l'
vehovmar
fuente
1
También para su información, los pasos bat hacen eco del comando que se ejecuta, por lo que debe iniciar los comandos bat con @ para obtener la salida (por ejemplo, "@dir").
Russell Gallop
21

Si desea obtener el stdout Y saber si el comando tuvo éxito o no, simplemente utilícelo returnStdouty envuélvalo en un controlador de excepciones:

tubería guionizada

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}")
}

salida :

[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

Desafortunadamente, a hudson.AbortException le falta algún método útil para obtener ese estado de salida, por lo que si se requiere el valor real, deberá analizarlo en el mensaje (¡uf!)

Contrariamente al Javadoc https://javadoc.jenkins-ci.org/hudson/AbortException.html, la compilación no falla cuando se detecta esta excepción. Se produce un error cuando se no atrapado!

Actualización: si también desea la salida STDERR del comando de shell, Jenkins desafortunadamente no admite adecuadamente ese caso de uso común. Un boleto 2017 JENKINS-44930 está atrapado en un estado de ping-pong obstinado mientras no avanza hacia una solución, considere agregar su voto positivo.

En cuanto a una solución ahora , podría haber un par de enfoques posibles:

a) Redirige STDERR a STDOUT 2>&1 , pero depende de ti analizar eso de la salida principal, y no obtendrás la salida si el comando falló, porque estás en el controlador de excepciones.

b) redirija STDERR a un archivo temporal (el nombre del cual preparó antes) 2>filename(pero recuerde limpiar el archivo después), es decir. el código principal se convierte en:

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) Vaya hacia el otro lado, configure en su returnStatus=truelugar, prescinda del controlador de excepciones y siempre capture la salida en un archivo, es decir:

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
}

Advertencia: el código anterior es específico de Unix / Linux: Windows requiere comandos de shell completamente diferentes.

Ed Randall
fuente
1
¿existe la posibilidad de obtener la salida como "ls: no se puede acceder a dir1: No existe tal archivo o directorio" y no solo "hudson.AbortException: el script devolvió el código de salida 2"?
user2988257
No veo cómo esto podría funcionar. En mis pruebas, el texto de salida nunca se asigna y esto es de esperar. La excepción lanzada desde el paso de shell impide que se asigne el valor de retorno
Jakub Bochenski
2
returnStatus y returnStdout no funcionan al mismo tiempo, lamentablemente. Aquí está el boleto. Por favor, vote: issues.jenkins-ci.org/browse/JENKINS-44930 .
Alexander Samoylov
1
@AlexanderSamoylov Debe solucionar el problema utilizando un archivo como en la opción (c) anterior. Desafortunadamente, los autores de estas herramientas a menudo son obstinados y no piensan en el futuro para otros casos de uso comunes, 'sh' aquí es un caso en cuestión.
Ed Randall
1
@Ed Randall, totalmente de acuerdo con usted. Es por eso que publiqué este problema con la esperanza de que, debido a la mayor cantidad de votos, comiencen a hacer algo.
Alexander Samoylov
12

¡Este es un caso de muestra, lo cual tendrá sentido, creo!

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
fuente
No sé cómo, pero su respuesta me ayudó mucho, gracias :)
shaharnakash
5

Para aquellos que necesitan usar la salida en comandos de shell posteriores, en lugar de groovy, se podría hacer algo como este ejemplo:

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

Encontré que los ejemplos en Code Maven son bastante útiles.

Nagev
fuente
-6

La manera más fácil es usarlo de esta manera

my_var=`echo 2` echo $my_var salida: 2

tenga en cuenta que no es simple comilla simple es comilla inversa (`).

Ajay Gadhavana
fuente
Votaron a favor, pero sugeriría que demuestren que estos deben estar envueltos debajo de un archivo que, de lo shcontrario, la gente podría pensar que es maravilloso, especialmente si no están familiarizados con las secuencias de comandos bash. Acabo de probarlo en Jenkins, usando en ls -llugar de echo 2y funciona. En realidad, había usado este enfoque antes, pero he estado buscando una alternativa porque no es muy confiable. Tengo la salida de un comando más complejo capturado en un shell estándar de esta manera, pero cuando se transfiere a Jenkins, shla variable no contiene nada, por alguna razón desconocida.
Nagev