Jenkins 管道 sh 似乎不尊重 shell 命令中的管道

Jenkins pipeline sh does not seem to respect pipe in shell command

我在 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 中正确使用管道命令的任何指导?

我终于考虑了一下,意识到可能是管道子壳导致了这个问题。我知道 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}"
}   

我也在为在我的 jenkins 管道中使用管道而苦苦挣扎,但作为旁注,如果你想要一种简单的方法来提取 maven pom 的版本,这是我在另一个 post 并且我正在使用:

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

pipeline-utility-steps plugin nowadays includes a readMavenPom步骤,允许按如下方式访问版本:

version = readMavenPom.getVersion()

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

script.sh:

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

詹金斯文件:

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

所以上面详细说明的任何内容对我使用脚本化的 Jenkinsfile 语法和 Groovy 都不起作用。但是,我能够让它工作。您使用的报价类型很重要。在下面的示例中,我试图从 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,它就不会工作——管道把一切都搞砸了。相反,我们将脚本用双引号引起来。

第一个 echo 打印 VERSION_NUMBER: 2.1.0 但第二个 echo 打印 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}"'
  }
}
...

第一个 echo 打印 VERSION_NUMBER: 2.1.0,第二个打印 VERSION_NUMBER: 2.1.0.

我知道这种迟到的答案,但是无论谁需要没有评估的解决方案,您都可以使用 /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但是这样pipe的子shell将由我们的男孩处理/bin/bash -c