【问题标题】:Jenkins: Pipeline sh bad substitution errorJenkins:管道 sh 错误替换错误
【发布时间】:2016-09-10 04:51:23
【问题描述】:

我的管道中的一个步骤将 .tar 上传到工件服务器。传入 env.BUILD_NUMBER 时出现错误替换错误,但是当数字被硬编码时,相同的命令有效。该脚本是通过jenkins 用groovy 编写的,在jenkins 工作区中运行。

sh 'curl -v --user user:password --data-binary ${buildDir}package${env.BUILD_NUMBER}.tar -X PUT "http://artifactory.mydomain.com/artifactory/release-packages/package${env.BUILD_NUMBER}.tar"'

返回错误:

[Pipeline] sh
[Package_Deploy_Pipeline] Running shell script
/var/lib/jenkins/workspace/Package_Deploy_Pipeline@tmp/durable-4c8b7958/script.sh: 2: 
/var/lib/jenkins/workspace/Package_Deploy_Pipeline@tmp/durable-4c8b7958/script.sh: Bad substitution
[Pipeline] } //node
[Pipeline] Allocate node : End
[Pipeline] End of Pipeline
ERROR: script returned exit code 2

如果在内部版本号中硬编码并换出${env.BUILD_NUMBER},我不会收到任何错误并且代码运行成功。

sh 'curl -v --user user:password --data-binary ${buildDir}package113.tar -X PUT "http://artifactory.mydomain.com/artifactory/release-packages/package113.tar"'

我在同一脚本的其他 sh 命令中使用 ${env.BUILD_NUMBER},在其他任何地方都没有问题。

【问题讨论】:

  • 我的猜测是 ${env.BUILD_NUMBER} 在其他实例中实际被 shell 看到之前被预处理步骤所取代。 sh 本身将其视为错误是正确的。
  • 在此行之前的同一函数中,我使用 ${env.BUILD_NUMBER} 以完全相同的方式上传到 Google 存储,这没有给我任何问题。编辑:我还制作了一个虚拟变量并将其设置为一个数字,然后传入变量并得到同样的问题。
  • 您很可能在整个 mrhaki.blogspot.com.au/2009/08/… 周围使用了错误的引号。此外,您不需要引用 curl 的 URL。所以也许在整个事情周围使用双引号将可以在没有任何单引号的情况下使用
  • 请显示使用${env.BUILD_NUMER} 没有问题的确切代码,因为错误是此类参数扩展的预期行为。
  • 你带我走上正确的道路,是单引号阻止了 env.BUILD_NUMBER 传递它的值。

标签: bash jenkins groovy jenkins-pipeline


【解决方案1】:

这原来是一个语法问题。将命令包装在' 中会导致传递${env.BUILD_NUMBER 而不是其值。我将整个命令包装在"s 中并逃脱了嵌套。现在工作正常。

sh "curl -v --user user:password --data-binary ${buildDir}package${env.BUILD_NUMBER}.tar -X PUT \"http://artifactory.mydomain.com/artifactory/release-packages/package${env.BUILD_NUMBER}.tar\""

【讨论】:

  • 是的,您成功了,感谢您的帮助。我不知何故错过了您的原始评论,希望我能早点阅读。
  • 与此问题没有直接关系,但请考虑使用 Artifactpry Pipeline DSL。它提供了比 curl cmd 执行更方便的 Groovy DSL,减少了语法错误的机会:wiki.jenkins-ci.org/display/JENKINS/…
  • @BW 使用 """ """
  • 只要 URL 中的任何变量被 Groovy 而不是由 shell 替换,您就可以在 URL 周围使用单引号而不是转义的双引号。
【解决方案2】:

为了将 groovy 参数传递到 Jenkins 管道中的 bash 脚本(有时会导致错误的替换)你有 2 个选项

三重双引号方式 [ " " " ] 要么 三单引号方式 [ ' ' ' ]

  1. 在三重双引号中你可以使用${someVariable}从groovy渲染普通参数,如果它是环境变量${env.someVariable},如果它是注入你的工作的参数${params.someVariable}

示例

     def YOUR_APPLICATION_PATH= "${WORKSPACE}/myApp/"

      sh """#!/bin/bash
      cd ${YOUR_APPLICATION_PATH}
      npm install
      """
  1. 在三个单引号中事情变得有点棘手,您可以将参数传递给环境参数并通过"\${someVaraiable}"使用它或使用连接groovy参数 em>''' + someVaraiable + '''

示例

   def YOUR_APPLICATION_PATH= "${WORKSPACE}/myApp/"

   sh '''#!/bin/bash
          cd ''' + YOUR_APPLICATION_PATH + '''
          npm install
    '''

   pipeline{
     agent { node { label "test" } }
     environment {
       YOUR_APPLICATION_PATH = "${WORKSPACE}/myapp/"
     }

     continue...
     continue...
     continue...

     sh '''#!/bin/bash
          cd "\${YOUR_APPLICATION_PATH}"
          npm install
    '''

    //OR
    sh '''#!/bin/bash
          cd "\${env.YOUR_APPLICATION_PATH}"
          npm install
    '''

【讨论】:

    【解决方案3】:

    实际上,您似乎误解了env 变量。在您的sh 块中,您应该直接访问${BUILD_NUMBER}

    原因/解释: env 表示脚本内部的环境。此环境可直接用于/可用于执行的任何操作,例如shell 脚本。

    还请注意不要给env.*写任何东西,而是使用withEnv{}块代替。

    【讨论】:

      【解决方案4】:

      通常最常见的问题是:

      错误替换

      错误是使用sh 而不是bash

      尤其是在使用 Jenkins 时,如果您使用的是 Execute shell,请确保您的 Commandshebang 开头,例如#!/bin/bash -xe#!/usr/bin/env bash

      【讨论】:

        【解决方案5】:

        我可以肯定地告诉你,这都是关于 sh shell 和 bash shell 的。我通过指定#!/bin/bash -xe 解决了这个问题,如下所示:

        node {
            stage("Preparing"){
                sh'''#!/bin/bash -xe
                    colls=( col1 col2 col3 )
                    for eachCol in ${colls[@]}
                    do
                      echo $eachCol
                    done
                '''
            }      
        }
        

        【讨论】:

          【解决方案6】:

          我在为 Amazon S3 应用上传的 Jenkins 管道上工作时遇到了同样的问题。

          我的脚本是这样的:

          pipeline {  
            agent any
            parameters {
              string(name: 'Bucket', defaultValue: 's3-pipeline-test', description: 'The name of the Amazon S3 Bucket')
              string(name: 'Prefix', defaultValue: 'my-website', description: 'Application directory in the Amazon S3 Bucket')
              string(name: 'Build', defaultValue: 'public/', description: 'Build directory for the application')
            }  
            stages {  
              stage('Build') {  
                steps {
                  echo 'Running build phase'
                  sh 'npm install' // Install packages
                  sh 'npm run build' // Build project 
                  sh 'ls' // List project files
                }  
              }  
              stage('Deploy') {  
                steps {  
                  echo 'Running deploy phase'
                  withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWSCredentials', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { 
                    sh 'aws s3 ls' // List AWS S3 buckets
                    sh 'aws s3 sync "${params.Build}" s3://"${params.Bucket}/${params.Prefix}" --delete' // Sync project files with AWS S3 Bucket project path
                  }  
                }   
              }
            }
            post { 
              success {  
                echo 'Deployment to Amazon S3 suceeded'  
              }  
              failure {  
                echo 'Deployment to Amazon S3 failed'  
              }  
            }  
          }
          

          这是我修复它的方法

          看到这是变量的插值调用,我不得不在这行脚本中替换单引号(''):

          sh 'aws s3 sync "${params.Build}" s3://"${params.Bucket}/${params.Prefix}" --delete' // Sync project files with AWS S3 Bucket project path
          

          到双引号(" "):

          sh "aws s3 sync ${params.Build} s3://${params.Bucket}/${params.Prefix} --delete" // Sync project files with AWS S3 Bucket project path
          

          所以我的脚本后来看起来像这样:

          pipeline {  
            agent any
            parameters {
              string(name: 'Bucket', defaultValue: 's3-pipeline-test', description: 'The name of the Amazon S3 Bucket')
              string(name: 'Prefix', defaultValue: 'my-website', description: 'Application directory in the Amazon S3 Bucket')
              string(name: 'Build', defaultValue: 'public/', description: 'Build directory for the application')
            }  
            stages {  
              stage('Build') {  
                steps {
                  echo 'Running build phase'
                  sh 'npm install' // Install packages
                  sh 'npm run build' // Build project 
                  sh 'ls' // List project files
                }  
              }  
              stage('Deploy') {  
                steps {  
                  echo 'Running deploy phase'
                  withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWSCredentials', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { 
                    sh 'aws s3 ls' // List AWS S3 buckets
                    sh "aws s3 sync ${params.Build} s3://${params.Bucket}/${params.Prefix} --delete" // Sync project files with AWS S3 Bucket project path
                  }  
                }   
              }
            }
            post { 
              success {  
                echo 'Deployment to Amazon S3 suceeded'  
              }  
              failure {  
                echo 'Deployment to Amazon S3 failed'  
              }  
            }  
          }
          

          就是这样

          我希望这会有所帮助

          【讨论】:

            【解决方案7】:

            我在 jar 文件的工件中显示 {env.MAJOR_VERSION} 时遇到问题。通过在 Jenkinsfile 中保持环境步骤来展示我的方法。

            pipeline {
            agent any
            environment {
            MAJOR_VERSION = 1
            }
            
            stages {
            stage('build') {
              steps {
                  sh 'ant -f build.xml -v'
             }
            }
            }
            post {
             always{
               archiveArtifacts artifacts: 'dist/*.jar', fingerprint: true
             }
            }
            }
            

            我解决了问题,然后在我的 Jenkins 构建输出中没有显示错误的替换。所以环境步骤在 Jenkinsfile 中扮演了更多的角色。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-07-14
              • 1970-01-01
              • 1970-01-01
              • 2019-02-23
              • 1970-01-01
              • 2019-02-28
              相关资源
              最近更新 更多