【问题标题】:How do I get the output of a shell command executed using into a variable from Jenkinsfile (groovy)?如何将使用 Jenkinsfile (groovy)中的变量执行的 shell 命令的输出?
【发布时间】:2016-08-01 13:19:36
【问题描述】:

我在 Jenkinsfile (Groovy) 上有类似的东西,我想将标准输出和退出代码记录在一个变量中,以便以后使用这些信息。

sh "ls -l"

我该怎么做?

【问题讨论】:

标签: groovy jenkins-pipeline


【解决方案1】:

最新版本的管道sh 步骤允许您执行以下操作;

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

另一个功能是returnStatus 选项。

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

这些选项是根据this 问题添加的。

sh 命令见官方documentation

对于声明式管道(参见 cmets),您需要将代码包装到 script 步骤中:

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

【讨论】:

【解决方案2】:

当前 Pipeline 版本原生支持 returnStdoutreturnStatus,这使得从 sh/bat 步骤获取输出或状态成为可能。

一个例子:

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

官方documentation

【讨论】:

【解决方案3】:

快速回答是这样的:

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

我认为存在一个能够获得 sh 步骤结果的功能请求,但据我所知,目前没有其他选择。

编辑:JENKINS-26133

EDIT2:不太确定从什么版本开始,但是 sh/bat 步骤现在可以返回 std 输出,简单地说:

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

【讨论】:

  • 另外仅供参考,bat 步骤会回显正在运行的命令,因此您需要使用 @ 启动 bat 命令才能获得输出(例如“@dir”)。
  • 我用output = sh(script: 'command here', returnStdout: true).trim().readLines().drop(1).join(" ")代替@
【解决方案4】:

如果您想获取标准输出并知道命令是否成功,只需使用 returnStdout 并将其包装在异常处理程序中:

脚本化管道

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

输出

[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

不幸的是 hudson.AbortException 缺少任何有用的方法来获取该退出状态,因此如果需要实际值,您需要从消息中解析它(呃!)

与 Javadoc https://javadoc.jenkins-ci.org/hudson/AbortException.html 相反,当捕获到此异常时,构建不是失败。当它没有被捕获时它会失败!

更新: 如果您还想要 shell 命令的 STDERR 输出,很遗憾 Jenkins 无法正确支持该常见用例。一张 2017 年的票 JENKINS-44930 陷入自以为是的乒乓状态,而在解决方案方面没有取得任何进展 - 请考虑为它投票。

至于现在的解决方案,可能有几种可能的方法:

a) 将 STDERR 重定向到 STDOUT 2>&1 - 但是你可以从主输出中解析它,如果命令失败,你将不会得到输出 - 因为你在异常处理程序中。

b) 将 STDERR 重定向到一个临时文件(您之前准备的名称)2>filename(但记得事后清理文件) - 即。主要代码变为:

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) 换一种方式,改为设置returnStatus=true,省去异常处理程序并始终将输出捕获到文件中,即:

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
}

警告:上面的代码是 Unix/Linux 特定的 - Windows 需要完全不同的 shell 命令。

【讨论】:

  • 是否有机会获得输出为“ls: cannot access dir1: No such file or directory”而不仅仅是“hudson.AbortException: script returned exit code 2”?
  • 我不明白这怎么可能奏效。在我的测试中,从未分配输出文本,这是可以预料的。从 shell 步骤抛出的异常阻止了返回值被分配
  • 不幸的是,returnStatus 和 returnStdout 不能同时工作。这是票。请投票:issues.jenkins-ci.org/browse/JENKINS-44930.
  • @AlexanderSamoylov 您必须使用上述选项 (c) 中的文件来解决问题。不幸的是,这些工具的作者经常固执己见,不会提前考虑其他常见用例,这里的“sh”就是一个很好的例子。
  • @Ed Randall,完全同意你的看法。这就是为什么我发布这个问题,希望由于更多的选票他们开始做点什么。
【解决方案5】:

这是一个示例案例,我相信这很有意义!

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


    echo "${commit[-1]} "

    }
}

【讨论】:

    【解决方案6】:

    对于那些需要在后续的 shell 命令中使用输出而不是 groovy 的人,可以这样做:

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

    我发现code maven 上的示例非常有用。

    【讨论】:

      【解决方案7】:

      以上所有方法都可以。但是要在代码中使用 var 作为环境变量,您需要先导出 var。

      script{
        sh " 'shell command here' > command"
        command_var = readFile('command').trim()
        sh "export command_var=$command_var"
      }
      

      用您选择的命令替换 shell 命令。现在,如果您使用的是 python 代码,您只需指定 os.getenv("command_var") 即可返回先前执行的 shell 命令的输出。

      【讨论】:

        【解决方案8】:

        如何在 groovy 中读取 shell 变量/如何将 shell 返回值分配给 groovy 变量。

        要求:打开一个文本文件使用shell读取行并将值存储在groovy中并获取每行的参数。

        这里是分隔符

        例如:releaseModule.txt

        ./APP_TSBASE/app/team/i-home/deployments/ip-cc.war/cs_workflowReport.jar,configurable-wf-report,94,23crb1,artifact
        
        
        
        ./APP_TSBASE/app/team/i-home/deployments/ip.war/cs_workflowReport.jar,configurable-temppweb-report,394,rvu3crb1,artifact
        

        =========================

        这里要获取模块名称第二个参数(configurable-wf-report),构建没有第三个参数(94),commit id 4th(23crb1)

        def  module = sh(script: """awk -F',' '{ print \$2 "," \$3 "," \$4 }' releaseModules.txt  | sort -u """, returnStdout: true).trim()
        
        echo module
        
        List lines = module.split( '\n' ).findAll { !it.startsWith( ',' ) }
        
        def buildid
        
        def Modname
        
        lines.each {
        
        List det1 =  it.split(',')
        
        buildid=det1[1].trim() 
        
        Modname = det1[0].trim()
        
        tag= det1[2].trim()
        
                       
        
        echo Modname               
        
        echo buildid
        
                        echo tag
        
                                
        
        }
        

        【讨论】:

          【解决方案9】:

          如果你没有单个 sh 命令,而是一个 sh 命令块,那么 returnstdout 将不起作用。

          我遇到了类似的问题,我应用了一些不是很干净的方法,但最终它起作用并达到了目的。

          解决方案 - 在 shell 块中,回显该值并将其添加到某个文件中。 在 shell 块外和脚本块内,读取此文件,修剪它并将其分配给任何本地/参数/环境变量。

          例子-

          steps {
               script {
                      sh '''
                         echo $PATH>path.txt
                         // I am using '>' because I want to create a new file every time to get the newest value of PATH
                      '''
                      path = readFile(file: 'path.txt')
                      path = path.trim() //local groovy variable assignment
          
                      //One  can assign these values to env and params as below  - 
                      env.PATH = path  //if you want to assign it to env var
                      params.PATH  = path //if you want to assign it to params var
          
          
               }
          }
          
          

          【讨论】:

            【解决方案10】:

            最简单的方法就是这样使用

            my_var=`echo 2` echo $my_var 输出 : 2

            注意不是简单的单引号是反引号(`)。

            【讨论】:

            • 赞成,但我建议您表明这些应该包含在 sh 下,否则人们可能会认为它很时髦,特别是如果他们不熟悉 bash 脚本。我刚刚在 Jenkins 上尝试过,使用 ls -l 而不是 echo 2 并且它有效。我之前实际上使用过这种方法,但一直在寻找替代方法,因为它不是很可靠。我以这种方式在标准 shell 上捕获了一个更复杂命令的输出,但是当移植到 Jenkins sh 时,由于某种未知原因,该变量什么也没有。
            猜你喜欢
            • 2022-10-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-23
            • 1970-01-01
            • 2010-09-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多