本文主要记录一下bash脚本中&
符号导致source命令执行后,变量无法取到值的问题原因及解决方法。
今天又是美好的一天,吃个早餐,改改代码,提测,打包、发版
、测试,齐活,喝杯茶,下班。整套动作行云流水,相当丝滑,直到有一天,不那么顺畅,出问题了。
先说下这套组合拳中的发版原理
,以下为简写版:
1、我们有一个 micro-config-server 的服务,配置文件为 application-template.yml
spring:
datasource:
url: ${databaseJdbcUrl}&useUnicode=true
2、项目 micro-config-server 下有一个启动jar的脚本,src/assembly/bin/bootstrap.sh
内容大致就是定义几个函数,例如start、stop、restart,然后以 nohoup 的方式启动
#!/bin/bash
source /opt/app/custom.sh
...
nohup $JAVACMD $JAVA_OPTS \
-DdatabaseJdbcUrl="$databaseJdbcUrl" \
-XX:+UseG1GC \
...
3、服务器上有一个自定义的文件 /opt/app/custom.sh
export databaseJdbcUrl=jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb
4、发版 micro-config-server 项目
交互流程:
(1)登录运维平台,发版,
首先会找到 micro-config-server 项目下 bootstrap.sh,执行start方法,启动jar
(2)执行 start 前,会通过 source 的方式将 custom.sh 导入到环境变量,
这样 bootstrap.sh 就能够拿到 databaseJdbcUrl 的值
(3)然后 bootstrap.sh 以 -D参数 的形式
把 customer.sh 的定义 databaseJdbcUrl 传递给 application-template.yml 中的$占位符,并进行赋值,
这样项目就能够跑起来了
说完了原理,简单说下背景情况:
事情是这样的,我们接了一个需求,做现有代码支持GaussDB数据库(原有DB是MySQL),这个需求由2个团队分别在2个分支上开发(甲做兼容改造、乙做功能开发)
甲团队先开发完,然后提测,在测试环境上一直运行着(此处有坑)。
乙团队新功能开发完毕,合并分支后,所有微服务要重新发版。
然而,悲剧发生了,
这个micro-config-server,总是找不到数据库连接地址,也就是databaseJdbcUrl 的值为空
。
起初以为是合并代码的问题,因为测试环境 micro-config-server 一直运行的好好的。但是,找到之前的安装包,手动执行 bootstrap.sh 脚本,依旧启动报错。奇了怪了,问题在哪里?
下面以一个实验说明,无法获取到 databaseJdbcUrl 的原因。
名称 | 说明 |
---|---|
CentOS | Linux shell环境 |
[root@ecs-1678 ~]# vim custom.sh
// 内容如下
export databaseJdbcUrl1=jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb
export databaseJdbcUrl2=jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb&1=1
export databaseJdbcUrl3='jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb&1=1'
[root@ecs-1678 ~]# chmod 755 custom.sh
[root@ecs-1678 ~]# source custom.sh
-bash: 1=1: command not found
[1]+ Done export databaseJdbcUrl2=jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb
[root@ecs-1678 ~]# echo $databaseJdbcUrl1
jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb
[root@ecs-1678 ~]# echo $databaseJdbcUrl2
[root@ecs-1678 ~]# echo $databaseJdbcUrl3
jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb&1=1
有没有发现什么问题?
(1)databaseJdbcUrl2 没有输出值
(2)source custom.sh 之后出现了一行Done…
为什么没有值? 经过和之前环境上的脚本对比,发现这个bash脚本中多了一个&1=1
经查询资料,在Bash脚本中,&符号用于将命令放入后台执行。当在命令的结尾加上&符号时,该命令将在后台执行,不会阻塞当前Shell进程,而是会立即返回提示符,让用户可以继续输入其他命令。
此时基本上离真相很近了,问题找到,解决的办法也简单,给databaseJdbcUrl的值加上单引号。
// 修改前 export databaseJdbcUrl=jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb
// 修改后 export databaseJdbcUrl='jdbc:postgresql://localhost:8000/acs_db?currentSchema=acs_symstatdb'
联系到了小明,custom.sh这个配置文件确实被改动了,添加了1=1
重点来了,测试环境能够跑起来是因为小明在启动 micro-config-server 时,是以手工传参
的方式执行了bootstrap.sh这个脚本把databaseJdbcUrl传了进去(类似于java -jar xxx.jar -DdatabaseJdbcUrl=…),而micro-config-server 自启动之后就再也没有重启过。
这乌龙搞得!所以修改公用通用的问题,一定要通知到相关项目组成员,进行方案评审。
如果本文内容对您有价值或者有启发的话,欢迎点赞、关注、评论和转发。您的反馈和陪伴将促进我们共同进步和成长。