我们经常使用一些模板语言来处理一些变量替换。比如jsp,php,velocity,freemarker,thymeleaf等。那对于shell来说,应该怎样替换变量呢。有一种很简单的办法可以做到。
先来看一个应用场景。在datax是阿里开源的一个异构数据源同步框架,其配置文档是json的,我想要用shell去调用执行pg到pg的数据同步,需要根据我的配置生成对应的配置文件。这如果用java来做就是维护一个对象,设置value,最后json-encode就好了。要是使用shell,这样也可以做到:
渲染脚本
#!/bin/bash
SRC_USER_NAME=etl
SRC_USER_PWD=etl
SRC_SQL="select * from tab"
SRC_HOST_IP="192.168.1.1"
SRC_HOST_PORT=3306
SRC_DB="abc_db"
TAR_USER_NAME="etl2"
TAR_USER_PWD="pass2"
fields_map=""a","b","c""
TAR_HOST_IP="aaaadfsdfdsfjsdjf"
TAR_HOST_PORT="5432"
TAR_DB="tar_db"
TAR_TABLENAME="tbname"
eval "cat <<EOF
$(< pg2pg.datax.json)
EOF
" > result.json
模板文档
pg2pg.datax.json
{
"job": {
"setting": {
"speed": {
"byte": 1048576
},
"errorLimit": {
"record": 0,
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "postgresqlreader",
"parameter": {
"username": "${SRC_USER_NAME}",
"password": "${SRC_USER_PWD}",
"where": "",
"connection": [
{
"querySql": [
"${SRC_SQL}"
],
"jdbcUrl": [
"jdbc:postgresql://${SRC_HOST_IP}:${SRC_HOST_PORT}/${SRC_DB}"
]
}
]
}
},
"writer": {
"name": "postgresqlwriter",
"parameter": {
"username": "${TAR_USER_NAME}",
"password": "${TAR_USER_PWD}",
"column": [
${fields_map}
],
"preSql": [
""
],
"connection": [
{
"jdbcUrl": "jdbc:postgresql://${TAR_HOST_IP}:${TAR_HOST_PORT}/${TAR_DB}",
"table": [
"${TAR_TABLENAME}"
]
}
]
}
}
}
]
}
}
输出结果
{
"job": {
"setting": {
"speed": {
"byte": 1048576
},
"errorLimit": {
"record": 0,
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "postgresqlreader",
"parameter": {
"username": "etl",
"password": "etl",
"where": "",
"connection": [
{
"querySql": [
"select * from tab"
],
"jdbcUrl": [
"jdbc:postgresql://192.168.1.1:3306/abc_db"
]
}
]
}
},
"writer": {
"name": "postgresqlwriter",
"parameter": {
"username": "etl2",
"password": "pass2",
"column": [
"a","b","c"
],
"preSql": [
""
],
"connection": [
{
"jdbcUrl": "jdbc:postgresql://aaaadfsdfdsfjsdjf:5432/tar_db",
"table": [
"tbname"
]
}
]
}
}
}
]
}
}
核心内容是
eval "cat <<EOF
$(< pg2pg.datax.json)
EOF
" > result.json
其中有几个语法需要学习下。
第一shell中变量的定义,变量赋值时,等号(=
)`两边必须没有空格。
第二, eval
的用法。
语法:eval cmdLine
eval会对后面的cmdLine进行两遍扫描,如果第一遍扫描替换变量,然后执行cmdLine.
[etl@data-server001 test]$ set 11 22 33 44
[etl@data-server001 test]$ echo $4
44
[etl@data-server001 test]$ echo $#
4
[etl@data-server001 test]$ echo "$$#"
$4
[etl@data-server001 test]$ eval echo "$$#"
44
本组测试中,echo可以读取变量的第一层定义,所以$#
代表参数个数4,$4
代表第4个参数44。但我们想要直接取最后一个参数,需要使用变量的值作为变量的value。eval就会再次扫描一遍。
第三,cat <<EOF
这是一个多行输入的操作。
[etl@data-server001 test]$ cat <<EOF
> aaa
> bbb
> ccc
> EOF
aaa
bbb
ccc
EOF代表End Of File,这里表示输入结束标志,<<EOF
表示定义结束符为EOF
,接下来直到输入EOF时,命令结束。cat就会把内容输出。cat本来是输出文件内容的,这里把输入当做临时文件处理了。
第四, $(xxx)
表示执行命令,和两个反引号的效果相同,会执行里面的命令。所以< pg2pg.datax.json
才会读取文件内容。
最后输入EOF结束内容。需要注意EOF前后不要有空格,必须是回车,不然就不代表最后一个字符了。
为了更加容易理解,可以修改渲染脚本
content=$(cat pg2pg.datax.json)
eval "cat <<EOF
$content
EOF" > result.json
- eval替换$content的值