This is my little newbie Groovy script, trying to cobble together a very beginner understanding of Jenkins Pipelines:

node {
 stage("hello") {
  def var = "val"
  echo "${var}"
  def stdout = sh( script: 'pwd', returnStdout: true ).trim()
  echo "${stdout}"
  def ret_status = sh( script: 'cd subdir', returnStatus: true )
  echo "${ret_status}"

The output is:

Started by user unknown or anonymous
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /home/user/workspace/newbie_dont_know_what_hes_doin
[Pipeline] {
[Pipeline] stage
[Pipeline] { (hello)
[Pipeline] echo
[Pipeline] sh
+ pwd
[Pipeline] echo
[Pipeline] sh
+ cd subdir
/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/script.sh: 1: cd: can't cd to subdir
[Pipeline] echo
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

The failure to cd subdir is intentional -- to demonstrate the mechanics of capturing shell stdout and status.

Why does the failed cd subdir report a returnStatus of 2 when cding to nonexistent directories in bash returns status-code 1? The Groovy documentation I have as reference says sh runs a Bourne shell script.

$ echo $SHELL
$ cd subdir
-bash: cd: subdir: No such file or directory
$ echo $?

1 Answer 1


There's a few things to note. First is that in the absense of a shebang being supplied, the actual interpreter that will be used is actually "sh", not "bash".

Taking a look at the Jenkins source for the BourneShellScript task, what happens is that Jenkins does not actually return the exit code from the subshell - it monitors a file called "jenkins-result.txt for changes (using a FileMonitoringTask) and when that file has been updated, it merely assigns the content of it as the exit status.

From what I can see, when you call "sh" it does not just run your script code, but actually wraps it in a fairly substantial block of boilerplate code (refer line 272 of the above linked BourneShellScript). In effect, something closer to this:

sh -c ({ while [ -d '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0' -a \\! -f '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-result.txt' ]; do touch '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-log.txt'; sleep 3; done } & jsc=durable-2fa119e0; JENKINS_SERVER_COOKIE=$jsc sh -xe '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/script.sh' > '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-output.txt' 2> 'jenkins-log.txt'; echo $? > '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-result.txt.tmp'; mv '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-result.txt.tmp' '/home/user/workspace/newbie_dont_know_what_hes_doin\@tmp/durable-2fa119e0/jenkins-result.txt'; wait) >&- 2>&- &

This is likely necessary to support scenarios where the node on which the script is executed is not the node on which the pipeline is executing, however it does leave a lot of room for error in returning the result, depending on the idiosyncracies of whatever interpreter "sh" is aliased to (not necessarily bash) and the OS on which it is running.

  • 1
    I agree with your assessment that this leaves a lot of room for error in returning the result. This raises the question, then: is there some other way to capture the true status code from a command? I'm still very much trying to get into the DevOps "headspace"...so I'm also trying to understand: what is that sh(..., returnStatus:true) meant to represent? I mean - your answer is clear: it's the return-code of the boilerplate wrapper...but why would a DevOps engineer want that value, as opposed to the returnStatus of his actual command? (Which seems the more natural interpretation)
    – StoneThrow
    May 14, 2021 at 15:42
  • This Q and A is a great illustration of the minefield that is Jenkins+groovy (IMNSHO).
    – qneill
    Jul 1, 2022 at 5:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.