我們經常使用一些模板語言來處理一些變量替換。比如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的值