Jenkins Groovy中Shell命令转换时间为秒始终返回相同值问题
Jenkins Groovy流水线时间转换问题排查
问题代码
用户编写的Groovy脚本化流水线代码:
def time1 = sh(script: 'date -u +"%Y-%m-%dT%H:%M:%S+00:00"', returnStdout: true).trim() def time1_sec = sh(script: 'date -d "$time1" +%s', returnStdout: true).trim() sleep(time: 30, unit: 'SECONDS') def time2 = sh(script: 'date -u +"%Y-%m-%dT%H:%M:%S+00:00"', returnStdout: true).trim() def time2_sec = sh(script: 'date -d "$time2" +%s', returnStdout: true).trim()
异常现象
控制台日志显示time1_sec和time2_sec返回相同值1709164800,且从日志可见执行date -d命令时未传入时间参数:
11:35:12 [Pipeline] sh 11:35:13 + date -u +%Y-%m-%dT%H:%M:%S+00:00 11:35:13 [Pipeline] sh 11:35:13 + date -d +%s 11:35:13 [Pipeline] sleep 11:35:13 Sleeping for 30 sec 11:35:43 [Pipeline] sh 11:35:44 + date -u +%Y-%m-%dT%H:%M:%S+00:00 11:35:44 [Pipeline] sh 11:35:45 + date -d +%s 11:35:45 [Pipeline] echo 11:35:45 time1 : 2024-02-29T16:35:13+00:00 11:35:45 [Pipeline] echo 11:35:45 time2 : 2024-02-29T16:35:44+00:00 11:35:45 [Pipeline] echo 11:35:45 time1_sec: 1709164800 11:35:45 [Pipeline] echo 11:35:45 time2_sec: 1709164800
而在Linux终端执行相同逻辑的Shell脚本可得到预期的不同结果:
#!/bin/bash # Script name: date-time-2-seconds.sh time1=$(date -u +"%Y-%m-%dT%H:%M:%S+00:00") time1_sec=$(date -d "$time1" +%s) sleep 30 time2=$(date -u +"%Y-%m-%dT%H:%M:%S+00:00") time2_sec=$(date -d "$time2" +%s) echo "time1 : ${time1}" echo "time2 : ${time2}" echo "time1_sec: ${time1_sec}" echo "time2_sec: ${time2_sec}"
执行输出:
$ ./date-time-2-seconds.sh time1 : 2024-02-29T16:52:58+00:00 time2 : 2024-02-29T16:53:28+00:00 time1_sec: 1709225578 time2_sec: 1709225608
问题原因
Groovy中单引号字符串不会进行变量插值,你编写的'date -d "$time1" +%s'里的$time1是Shell层面的变量,但每个sh步骤都会启动全新的Shell进程,该进程中未定义time1变量,因此Shell执行时$time1会被解析为空字符串,最终执行的命令变成date -d +%s。
当date -d未传入有效时间参数时,会返回一个固定的默认时间戳(如你看到的1709164800),导致两次结果相同。
修复方案
方案1:使用Groovy双引号实现变量插值
把Shell命令用Groovy双引号包裹,让Groovy先将$time1替换为实际值,再传递给Shell执行:
def time1 = sh(script: 'date -u +"%Y-%m-%dT%H:%M:%S+00:00"', returnStdout: true).trim() def time1_sec = sh(script: "date -d '$time1' +%s", returnStdout: true).trim() sleep(time: 30, unit: 'SECONDS') def time2 = sh(script: 'date -u +"%Y-%m-%dT%H:%M:%S+00:00"', returnStdout: true).trim() def time2_sec = sh(script: "date -d '$time2' +%s", returnStdout: true).trim()
这里用单引号包裹时间字符串,避免Groovy转义问题,同时确保Shell能正确解析时间格式。
方案2:合并Shell命令,减少进程调用
一次Shell调用同时获取格式化时间和时间戳,避免跨Shell传递变量的问题,效率更高:
// 一次调用拿到格式化时间和时间戳,用空格分隔 def time1_data = sh(script: 'date -u +"%Y-%m-%dT%H:%M:%S+00:00 %s"', returnStdout: true).trim().split() def time1 = time1_data[0] def time1_sec = time1_data[1] sleep(time: 30, unit: 'SECONDS') def time2_data = sh(script: 'date -u +"%Y-%m-%dT%H:%M:%S+00:00 %s"', returnStdout: true).trim().split() def time2 = time2_data[0] def time2_sec = time2_data[1]
内容的提问来源于stack exchange,提问作者Rafiq
相关产品推荐
相关产品推荐

