You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jenkins Pipeline美元符号相关编译错误修复求助

Jenkins Pipeline S3 文件同步问题修复方案

问题背景

需求为:从AWS账号A的S3存储桶下载最新文件,检查AWS账号B的S3存储桶是否存在该文件,不存在则复制过去。执行Pipeline时出现Groovy编译错误:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 82: illegal string body character after dollar sign;
solution: either escape a literal dollar sign "$5" or bracket the value expression "${5}" @ line 50, column 131.
sort |tail -n 1 |awk '{print $4}'"""

错误原因

核心是Groovy字符串插值规则引发的问题:

  • 双引号字符串中,$会被解析为Groovy变量的开头,因此awk命令里的$4需要转义,否则Groovy会将其视为未定义变量。
  • 三重单引号字符串不会解析Groovy变量,导致命令中的${params.xxx}、${env.xxx}无法被正确替换。
  • 代码存在语法错误(如路径引号不匹配)、Groovy语法使用不当(如exec函数参数写法、if判断变量引用)等问题。

修复后的完整代码

def exec(String commandString) {
  return sh(script: commandString, returnStdout: true).trim()
}

pipeline {
  agent {
    docker {
      image 'xxx.dkr.ecr.us-west-2.amazonaws.com/abc/base-image:xxxx.xxx'
      registryUrl 'https://xxx.dkr.ecr.us-west-2.amazonaws.com'
      registryCredentialsId 'ecr:us-west-2:xxx'
      label 'docker-preemptive'
    }
  }
  parameters {
    string(
      defaultValue: 'abc',
      description: '源S3存储桶名称',
      name: 'src_bucket')
    string(
      defaultValue: 'abc_src_path',
      description: '源S3路径前缀',
      name: 'src_base_location')
    string(
      defaultValue: 'xyz',
      description: '目标S3存储桶名称',
      name: 'dst_bucket')
    string(
      defaultValue: 'xyz_dst_path',
      description: '目标S3路径前缀',
      name: 'dst_base_location')
    string(
      defaultValue: 'file_location',
      description: '文件所在子路径',
      name: 'file_location')
  }
  environment {
    AWS_DEFAULT_REGION = "us-west-2"
    SRC_LATEST_S3_FILE = ''
    DST_LATEST_S3_FILE = ''
  }
  stages {
    stage ("下载源S3桶最新文件") {
      steps {
        script {
          withCredentials([[
            $class: 'AmazonWebServicesCredentialsBinding',
            credentialsId: "abc_src_iam_credentials",
            accessKeyVariable: 'ABC_S3_ACCESS_KEY_ID',
            secretKeyVariable: 'ABC_S3_SECRET_ACCESS_KEY'
          ]]) { 
            // 获取源桶最新文件名,转义awk中的$4避免Groovy解析
            def cmd = "aws s3 ls s3://${params.src_bucket}/${params.src_base_location}/${params.file_location}/ | sort | tail -n 1 | awk '{print \$4}'"
            echo "$cmd"
            echo "正在获取源S3桶的最新文件名"
            env.SRC_LATEST_S3_FILE = exec(cmd)
            echo "源桶最新文件: ${env.SRC_LATEST_S3_FILE}"
            
            // 验证源桶是否存在文件
            if (env.SRC_LATEST_S3_FILE.isEmpty()) {
              echo "源S3桶中没有找到文件"
            } else {
              echo "正在下载最新文件到本地"
              def src_cp_cmd = "aws s3 cp s3://${params.src_bucket}/${params.src_base_location}/${params.file_location}/${env.SRC_LATEST_S3_FILE} ${env.SRC_LATEST_S3_FILE}"
              exec(src_cp_cmd)
            }
          }
        }
      }
    }
    stage ("验证并同步到目标S3桶") {
      when {
        not {
          expression { env.SRC_LATEST_S3_FILE.isEmpty() }
        }
      }
      steps {
        script {
          withCredentials([[
            $class: 'AmazonWebServicesCredentialsBinding',
            credentialsId: "xyz-dst-s3-creds",
            accessKeyVariable: 'S3_DST_ACCESS_KEY_ID',
            secretKeyVariable: 'S3_DST_SECRET_ACCESS_KEY'
          ]]) { 
            // 修复路径引号问题,转义awk中的$4
            def cmd1 = "aws s3 ls \"s3://${params.dst_bucket}/${params.dst_base_location}/${params.file_location}/\" | sort | tail -n 1 | awk '{print \$4}'"
            env.DST_LATEST_S3_FILE = exec(cmd1)
            
            // 对比源文件与目标桶最新文件
            if (env.SRC_LATEST_S3_FILE == env.DST_LATEST_S3_FILE) {
              echo "目标S3桶中已存在该最新文件"
            } else {
              echo "目标S3桶中未找到最新文件,开始同步"
              def dst_cp_cmd = "aws s3 cp ${env.SRC_LATEST_S3_FILE} \"s3://${params.dst_bucket}/${params.dst_base_location}/${params.file_location}/${env.SRC_LATEST_S3_FILE}\""
              exec(dst_cp_cmd)
            } 
          }
        }
      }
    }
  }
}

关键修复点说明

  • exec函数优化:移除多余的${}包裹,直接使用commandString参数。
  • awk命令转义:将所有awk中的$4转义为\$4,避免Groovy将其解析为变量。
  • 字符串类型调整:将原本用三重单引号的命令字符串改为双引号,确保Groovy变量能被正确插值。
  • 路径语法修复:修正目标桶ls命令中的引号不匹配问题,确保路径格式正确。
  • 变量引用优化:if判断中直接使用变量名,移除多余的${}包裹。
  • 参数引用修正:将${file_location}改为${params.file_location},确保从Pipeline参数中正确取值。

内容的提问来源于stack exchange,提问作者devops learner1986

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 02:18:19