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
相关产品推荐
相关产品推荐

