【问题标题】:Jenkins pipeline sh does not seem to respect pipe in shell commandJenkins 管道 sh 似乎不尊重 shell 命令中的管道
【发布时间】:2017-07-22 22:23:53
【问题描述】:

我在 2.32.2 版本的管道中使用 Jenkinsfile。

出于各种原因,我想从 pom.xml 中提取版本字符串。我希望我不必添加 Maven 帮助插件并使用评估。

我很快想出了一个小 sed 表达式,将它从 pom 中取出,它使用管道并在执行程序的 jenkins 工作区中的命令行上工作。

$ sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g' 1.0.0-SNAPSHOT

它可能会被优化,但我想了解为什么管道似乎在管道 sh 命令上失败。我玩过各种字符串格式,目前正在使用美元斜线字符串。

为了方便输出命令字符串,流水线步骤如下所示:

script {
    def ver_script = $/sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g'/$
    echo "${ver_script}"
    POM_VERSION = sh(script: "${ver_script}", returnStdout: true)
    echo "${POM_VERSION}"
}

当在 jenkins 管道中运行时,我得到以下控制台输出,其中似乎将管道命令分离为单独的命令:

[Pipeline] script
[Pipeline] {
[Pipeline] echo
sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g'
[Pipeline] sh
[FRA-198-versioned-artifacts-44SD6DBQOGOI54UEF7NYE4ECARE7RMF7VQYXDPBVFOHS5CMSTFLA] Running shell script
+ sed -n /<version>/,/<version/p pom.xml
+ head -1
+ sed s/[[:blank:]]*<\/*version>//g
sed: couldn't write 89 items to stdout: Broken pipe
[Pipeline] }
[Pipeline] // script

关于如何在 jenkinsfile 中正确使用管道命令的任何指导?

【问题讨论】:

    标签: jenkins pipe jenkins-pipeline subshell


    【解决方案1】:

    我终于考虑了一下,并意识到管道子壳可能是导致问题的原因。我知道 eval 的一些弊端,但我最终将其包装在 eval 中:

    script {
        def ver_script = $/eval "sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g'"/$
        echo "${ver_script}"
        POM_VERSION = sh(script: "${ver_script}", returnStdout: true)
        echo "${POM_VERSION}"
    }   
    

    【讨论】:

      【解决方案2】:

      我知道这种迟到的答案,但是任何需要没有评估的解决方案的人都可以使用/bin/bash -c "script" 来制作管道

      script {
          POM_VERSION = sh(script: "/bin/bash -c 'sed -n \'/<version>/,/<version/p\' pom.xml | head -1 | sed \'s/[[:blank:]]*<\/*version>//g\'\''", returnStdout: true)
          echo "${POM_VERSION}"
      }
      

      这种方法的唯一问题是hellish escape 但是这样管道的子shell 将由我们的男孩/bin/bash -c 处理

      【讨论】:

        【解决方案3】:

        如果您的环境允许,我找到了一个简单的解决方案,就是将包含管道的脚本放入文件中,然后使用 sh 运行,如下所示:

        script.sh

        #!/bin/sh
        kubectl exec --container bla -i $(kubectl get pods | awk '/foo-/{ print $1 }') -- php /code/dostuff
        

        Jenkinsfile

        stage('Run script with pipes') {
          steps {
            sh "./script.sh"
          }
        }
        

        【讨论】:

          【解决方案4】:

          pipeline-utility-steps 插件现在包含一个readMavenPom 步骤,它允许访问版本如下:

          version = readMavenPom.getVersion()
          

          【讨论】:

          • 谢谢,完成最终目标的绝佳指针,但我通常对 sh 步骤中管道的行为感到好奇。花了我一分钟时间来考虑子脱壳,而不是我在文档中看到的或者在尝试执行快速而肮脏的 sh 步骤时立即想到的问题。
          • @sporkthrower 你有没有更多地了解潜在的子炮击或该行为背后的解释?
          • 不是真的,你的例子很复杂,不确定是因为子壳还是一些语法怪异
          【解决方案5】:

          因此,使用 Groovy 的脚本化 Jenkinsfile 语法对我来说没有任何上述详细信息。但是,我能够让它工作。您使用的引用类型很重要。在下面的示例中,我尝试从 GitHub 获取最新的 git 标签。

          ...
          
          stage("Get latest git tag") {
            if (env.CHANGE_BRANCH == 'master') {
              sh 'git fetch --tags'
              TAGGED_COMMIT = sh(script: 'git rev-list --branches=master --tags --max-count=1', returnStdout: true).trim()
              LATEST_TAG = sh(script: 'git describe --abbrev=0 --tags ${TAGGED_COMMIT}', returnStdout: true).trim()
              VERSION_NUMBER = sh(script: "echo ${LATEST_TAG} | cut -d 'v' -f 2", returnStdout: true).trim()
              echo "VERSION_NUMBER: ${VERSION_NUMBER}"
              sh 'echo "VERSION_NUMBER: ${VERSION_NUMBER}"'
            }
          }
          ...
          

          注意分配LATEST_TAG 的shell 执行如何按预期工作(将变量分配给v2.1.0)。如果我们尝试同样的事情(用单引号)来分配VERSION_NUMBER,它就行不通了——管道把一切都搞砸了。相反,我们将脚本用双引号括起来。

          第一个回显打印VERSION_NUMBER: 2.1.0,但第二个打印VERSION_NUMBER:。如果您希望VERSION_NUMBER 在shell 命令中可用,则必须将shell 命令的输出分配给env.VERSION_NUMBER,如下所示:

          ...
          
          stage("Get latest git tag") {
            if (env.CHANGE_BRANCH == 'master') {
              sh 'git fetch --tags'
              TAGGED_COMMIT = sh(script: 'git rev-list --branches=master --tags --max-count=1', returnStdout: true).trim()
              LATEST_TAG = sh(script: 'git describe --abbrev=0 --tags ${TAGGED_COMMIT}', returnStdout: true).trim()
              env.VERSION_NUMBER = sh(script: "echo ${LATEST_TAG} | cut -d 'v' -f 2", returnStdout: true).trim()
              echo "VERSION_NUMBER: ${VERSION_NUMBER}"
              sh 'echo "VERSION_NUMBER: ${VERSION_NUMBER}"'
            }
          }
          ...
          

          第一个回显打印VERSION_NUMBER: 2.1.0,第二个打印VERSION_NUMBER: 2.1.0

          【讨论】:

            【解决方案6】:

            我也在为在我的 jenkins 管道中使用管道而苦苦挣扎,但作为旁注,如果你想要一种简单的方法来提取 maven pom 的版本,这是我在另一篇文章中找到的一个非常干净的方法,我正在使用:

            stage('Preparation') {
             version = getVersion()
             print "version : " + version
            }
            def getVersion() {
              def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
              matcher ? matcher[0][1] : null
            }
            

            给你:

            [Pipeline] echo
            releaseVersion : 0.1.24
            [Pipeline] sh
            

            【讨论】:

            • 没有必要再做这种事了。使用readMavenPom 步骤查看我的答案。
            • 太棒了!我会立即使用它。谢谢
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-11-06
            • 1970-01-01
            • 2013-07-07
            相关资源
            最近更新 更多