【问题标题】:How to use shell regular expression in jenkinsfile for jenkins pipeline?如何在 jenkins 管道中使用 jenkinsfile 中的 shell 正则表达式?
【发布时间】:2018-10-26 16:09:46
【问题描述】:

我正在尝试用 jenkinsfile 中的“_”替换 Git 分支名称中的“/”,以便我可以用分支名称标记我的 docker 映像。在 bash 中,以下命令可以正常工作

echo "${git_branch_name//\//_}"

但是当在 jenkinsfile 中使用上面的命令时,它会抛出一个错误。

    #!/usr/bin/env groovy

    def commit_id
    def imagetag
    def branch_name
    def git_branch_name
    node('Nodename') {

        stage('checkout') {
            checkout (scm).$Branch_Param
            sh "git rev-parse --short HEAD > .git/commit-id"
            commit_id = readFile('.git/commit-id').trim()
            sh "git rev-parse --abbrev-ref HEAD > .git/branch-name"
            git_branch_name = readFile('.git/branch-name').trim()
            branch_name= sh "echo ${git_branch_name//\//_}"
            sh "echo ${commit_id}"
            sh "echo ${branch_name}"
            sh "echo Current branch is ${branch_name}"
        }

    }

WorkflowScript: 15: end of line reached within a simple string 'x' or "x" or /x/;
   solution: for multi-line literals, use triple quotes '''x''' or """x""" or /x/ or $/x/$ @ line 15, column 28.
        sh "branch_name = echo ${git_branch_name//\//_}"

我在这里做错了什么?我应该使用 Groovy 正则表达式而不是 shell 吗?为什么 shell 没有被正确解释?

谢谢

【问题讨论】:

  • 我能够使用 groovy branch_name = git_branch_name.replaceAll("/", "_") 来解决这个问题,但我想知道乳清壳解释不起作用
  • 因为sh 是 Bourne shell 而不是 Bash,也许吧? documentation 表示您可以选择带有 shebang 行的解释器,所以添加 #!/bin/bash 可能会起作用吗? sh 不理解 ${parameter//pattern/replacement} 扩展,只有 Bash 可以。

标签: jenkins sh jenkins-pipeline


【解决方案1】:

问题是您要求 Groovy 本身解释表达式 ${git_branch_name//\//_},而不是 shell。在传递给sh 步骤的字符串周围使用双引号是导致这种情况的原因。因此,如果您改为编写以下内容,则第一个错误将消失:

    sh 'echo ${git_branch_name//\\//_}' // <- Note the single-quotes

基本上,除非您特别需要使用 groovy 的 字符串插值,否则请始终使用单引号(请参阅此答案底部的最后一个 echo)。

有趣的是,当我测试时,我似乎不需要 shebang (#!/bin/bash) 来指定 bash,正如一些 cmets 所建议的那样;这个${variable//x/y} 替换语法在 sh 步骤中按原样工作。我猜产生的shell是bash。我不知道是不是一直都是这样,或者我们的 Jenkins 盒子是不是专门这样设置的。

还请注意,您需要转义转义序列 ('\\/'),因为您传递给 sh 步骤的是 groovy 代码中的字符串文字。如果你不添加额外的反斜杠,传递给 shell 以由它解释的行将是 echo ${git_branch_name////_},它不会理解。

但还有其他问题。首先,像您一样将 sh 步骤的输出分配给 branch_name 意味着 branch_name 将始终等于 null。要从一行 shell 代码中获取标准输出,您需要将额外参数 returnStdout: true 传递给 sh:

branch_name = sh (
    script: 'echo ${git_branch_name//\\//_}',
    returnStdout: true
).trim ()     // You basically always need to use trim, because the
              // stdout will have a newline at the end

对于奖励积分,我们可以将该 sh 调用包装在 closure 中。我发现自己经常使用它来使这成为一个好主意。

// Get it? `sh` out, "shout!"
def shout = { cmd -> sh (script: cmd, returnStdout: true).trim () }

//...

branch_name = shout 'echo ${git_branch_name//\\//_}'

但最后,主要问题是 bash(或实际产生的任何 shell)无法访问 groovy 变量。据它所知,echo ${git_branch_name} 输出一个空字符串,因此echo ${git_branch_name//\//_} 也是如此。

你有几个选择。你可以跳过.git/branch-name的创建,直接输出git rev-parse的字符串替换结果:

branch_name = shout 'name=$(git rev-parse --abbrev-ref HEAD) && echo ${name//\\//_}'

或者为了进一步简化,你可以使用 groovy 的字符串替换函数而不是 bash 语法:

branch_name = shout ('git rev-parse --abbrev-ref HEAD').replace ('/', '_')

就个人而言,我发现后者更具可读性。 YMMV。所以最后把它们放在一起:

#!groovy

def shout = { cmd -> sh (script: cmd, returnStdout: true).trim () }

// Note that I'm not declaring any variables up here. They're not needed.
// But you can if you want, just to clearly declare the environment for
// future maintainers.

node ('Nodename') {
    stage ('checkout') {
        checkout (scm).$Branch_Param
        commit_id = shout 'git rev-parse --short HEAD'
        branch_name = shout ('git rev-parse --abbrev-ref HEAD').replace ('/', '_')

        echo commit_id
        echo branch_name
        echo "The most recent commit on branch ${branch_name} was ${commit_id}"
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 2016-06-25
    • 1970-01-01
    相关资源
    最近更新 更多