Jenkins:管道 sh 错误替换错误

Posted

技术标签:

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

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

sh 'curl -v --user user:password --data-binary $buildDirpackage$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 $buildDirpackage113.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 传递它的值。 【参考方案1】:

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

sh "curl -v --user user:password --data-binary $buildDirpackage$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 个选项

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

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

示例

     def YOUR_APPLICATION_PATH= "$WORKSPACE/myApp/"

      sh """#!/bin/bash
      cd $YOUR_APPLICATION_PATH
      npm install
      """
    在三个单引号中事情变得有点棘手,您可以将参数传递给环境参数并通过"\$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 中扮演了更多的角色。

【讨论】:

以上是关于Jenkins:管道 sh 错误替换错误的主要内容,如果未能解决你的问题,请参考以下文章

Jenkins报错Caused: java.io.IOException: Cannot run program "sh" (in directory "D:Jenki(

解决Jenkins权限配置错误,导致登录时出现没有Overall/read权限

Jenkins 管道错误:java.lang.NoSuchMethodError:找不到这样的 DSL 方法“withSonarQubeEnv”

Jenkins 管道 sh 在 Windows 上因“无法运行程序 nohup”而失败

Jenkins

Jenkins