Keywords: Jenkins Pipeline | Shell Command Output | returnStdout | returnStatus | Groovy Script
Abstract: This article provides a comprehensive guide on capturing shell command standard output and exit status codes in Jenkins pipelines. Through detailed analysis of the sh step's returnStdout and returnStatus parameters, combined with practical code examples, it demonstrates effective methods for handling command execution results in both declarative and scripted pipelines. The article also explores security considerations of variable interpolation and best practices for error handling, offering complete technical guidance for Jenkins pipeline development.
Introduction
In modern continuous integration/continuous deployment (CI/CD) workflows, Jenkins pipelines serve as core components for automated building and deployment, valued for their flexibility and extensibility. However, many developers face a common challenge: how to capture shell command output during pipeline execution and store it in variables for subsequent use. This article starts from fundamental concepts and delves deeply into the mechanisms for capturing shell command output in Jenkins pipelines.
Basic Principles of Shell Command Execution
In Jenkins pipelines, the sh step is the primary method for executing shell commands. Traditional simple usage like sh "ls -l" can execute commands but cannot retrieve command execution results. This limitation often complicates pipeline logic, forcing developers to seek alternative solutions for output capture.
From the evolution perspective of Jenkins pipelines, early versions indeed had restrictions on Groovy code execution. However, with continuous improvements in pipeline functionality, the current sh step now provides rich parameter options, making output capture straightforward and simple.
Using the returnStdout Parameter
The returnStdout parameter is the key option for capturing command standard output. When set to true, the sh step returns the standard output content of command execution. Here is a typical usage example:
GIT_COMMIT_EMAIL = sh (
script: 'git --no-pager show -s --format=\'%ae\'',
returnStdout: true
).trim()
echo "Git committer email: ${GIT_COMMIT_EMAIL}"
In this example, we execute a Git command to obtain the committer's email address. returnStdout: true ensures the command output is captured and assigned to the GIT_COMMIT_EMAIL variable. Note that since command output typically includes newline characters, using the trim() method to remove leading and trailing whitespace is a common practice.
Application of the returnStatus Parameter
Beyond capturing standard output, command exit status codes are crucial execution information. The returnStatus parameter is specifically designed to retrieve command exit status codes:
BUILD_FULL = sh (
script: "git log -1 --pretty=%B | grep '\\[jenkins-full]'",
returnStatus: true
) == 0
echo "Build full flag: ${BUILD_FULL}"
This example demonstrates how to check if a Git commit message contains a specific flag. If the grep command finds matching content (exit status code 0), BUILD_FULL is set to true; otherwise, it remains false.
Special Handling in Declarative Pipelines
In declarative pipelines, due to syntax limitations, direct variable assignment might encounter issues. In such cases, the script step must be used to wrap Groovy code:
script {
GIT_COMMIT_EMAIL = sh (
script: 'git --no-pager show -s --format=\'%ae\'',
returnStdout: true
).trim()
echo "Git committer email: ${GIT_COMMIT_EMAIL}"
}
This wrapping approach ensures that flexible Groovy code logic can still be executed within the strict structure of declarative pipelines.
Variable Interpolation and Security Considerations
When using variables in shell commands, string interpolation security requires special attention. Reference article 1 mentions the risk of "injection via interpolation," a security vulnerability that could be exploited maliciously.
The safe approach is to use single-quoted strings to avoid Groovy variable interpolation, or pass parameters through environment variables:
script {
env.BUILD_ID = "1234"
env.BUILD_NUMBER = "5678"
}
sh '''#!/bin/bash
echo "$BUILD_ID"
varName="BUILD_NUMBER"
echo "${!varName}"
'''
This method not only enhances security but also makes shell script logic clearer.
Error Handling and Output Capture
In practical applications, command execution might fail, but developers still need to capture output information during failures. Reference article 2 discusses this common issue: when a command returns a non-zero status code, the pipeline might abort execution, and even with returnStdout: true set, output might not be captured correctly.
An effective solution is to capture output through file redirection:
status = bat returnStatus: true, script: 'command > output'
if (status != 0) {
result = readJSON('output')
// Handle error scenario
}
Although this method adds complexity with file operations, it ensures reliable command output retrieval under various execution states.
Multi-line Commands and String Processing
For complex multi-line commands, triple quotes can be used to maintain command readability:
def path = 'test.txt'
def output = bat returnStdout: true, script: """
deploy command that may fail "$path"
echo Finished
"""
output = output.substring(0, output.lastIndexOf('Finished'))
This format supports variable interpolation while maintaining clear command structure, making it particularly suitable for complex deployment scripts.
Best Practices Summary
Based on the above analysis, we summarize best practices for capturing shell command output in Jenkins pipelines:
- Define Requirements Clearly: Choose between
returnStdoutandreturnStatus, or use both combined, based on specific scenarios. - Prioritize Security: Avoid Groovy variable interpolation in shell commands whenever possible; prefer passing parameters through environment variables.
- Implement Error Handling: For commands that might fail, consider using file redirection or additional commands to ensure reliable output capture.
- Ensure Code Readability: Use appropriate string formats and code structures to maintain pipeline script maintainability.
- Conduct Testing and Validation: Thoroughly test various execution scenarios before actual deployment to ensure correctness of output capture logic.
Conclusion
Capturing shell command output in Jenkins pipelines is a seemingly simple yet practically complex technical challenge. By properly utilizing the parameter options of the sh step, combined with appropriate security measures and error handling mechanisms, developers can build robust and reliable CI/CD pipelines. The methods and best practices introduced in this article provide comprehensive technical guidance for solving this common problem, aiming to help readers better apply these techniques in real-world projects.
As the Jenkins ecosystem continues to evolve, we anticipate more tools and methods that simplify and optimize this process in the future. Until then, mastering existing technical solutions and understanding their underlying principles remains an essential skill for every Jenkins pipeline developer.