shell读取配置文件参数

# 简介

在shell脚本中读取配置文件使用 cat ,grep,awk和sed

# .env文件读取

# .env文件

# define env var default value.
# -------------开发环境-------------
# compose命令
COMPOSE_CMD=docker-compose

# -------------正式环境-------------
# compose命令
# COMPOSE_CMD=docker-compose
COMPOSE_CMD=config/docker/docker-compose
SAMY=SAMY11
SAMY2 = SAMY21111
1
2
3
4
5
6
7
8
9
10
11

# bash文件

#!/bin/bash
env_file=$(pwd)/env/master.env
function get_config(){
  param=$1
  # value=$(sed -E '/^#.*|^ *$/d' $env_file | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  value=$(sed -E '/^#.*|^ *$/d' $env_file | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  echo $value
}
compose_cmd=$(get_config COMPOSE_CMD)
echo $compose_cmd
SAMY=$(get_config SAMY)
echo $SAMY

SAMY2=$(get_config SAMY2)
echo $SAMY2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sh env.sh
config/docker/docker-compose
SAMY11
SAMY21111
1
2
3
4

# 原理解析【要点】

  • 读取配置文件时,将注释符#标记的行和空行全部过滤 sed -E '/^#.*|^ *$/d' $env_file
  • 使用sed s/[[:space:]]//g 去除 多余的空格符;
  • 匹配指定参数相符的行并截取对应的值 awk -F "${param}=" "/${param}=/{print \$2}"
  • 获得的结果可能有多个,取最后一个值 tail -n1
  • 这4步都用 | 管道符连接,其作用是将当前的结果作为下一条命令的输入,连续处理以达到获取参数的最终目的;

# 全部替换 【要点】

my.cnf配置替换;

sed -i "s/\(skip-grant-tables\).*/#skip-grant-tables/" /etc/my.cnf
1

# env实践【要点】

default.env

# define env var default value.
# 一定默认生产线上环境
isPro = false

# 默认是中文版本
dafaultLang = en

# 只有英文版本
isOnlyEn = false
1
2
3
4
5
6
7
8
9

en.env

# 英文版本环境

# 只有英文版本
isOnlyEn = true
1
2
3
4

env.sh脚本

###
# @Author: samy
# @email: yessz#foxmail.com
# @time: 2021-11-16 16:41:46
 # @modAuthor: samy
 # @modTime: 2021-11-18 19:03:25
# @desc:
# Copyright © 2015~2021 BDP FE
###
#!/bin/bash
defaultFile=$(pwd)/env/default.env
targetFile=$(pwd)/env/en.env

curDir=$(cd "$(dirname "$0")"; pwd)
webDir=${curDir}/web

function getEnvConf() {
  param=$1
  value1=$(sed -E '/^#.*|^ *$/d' $defaultFile | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  value2=$(sed -E '/^#.*|^ *$/d' $targetFile | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  if [ ! -n "$value2" ]; then
    value=$value1
  else
    value=$value2
  fi
  echo $value
}

replacePro() {
  varKey=$2
  defaultValue=$3
  varValue=$4
  echo $varKey $varValue $defaultValue
  if [[ "$OSTYPE" == "darwin"* ]]; then
      sed -i "" "s#^var ${varKey} = ${defaultValue}.*#var ${varKey} = ${varValue};#g" $1
  else
      sed -i "s#^var ${varKey} = ${defaultValue}.*#var ${varKey} = ${varValue};#g" $1
  fi
}

isPro=$(getEnvConf isPro)
dafaultLang=$(getEnvConf dafaultLang)
isOnlyEn=$(getEnvConf isOnlyEn)

replacePro ${webDir}/app/conf.js isPro false  $isPro 
replacePro ${webDir}/app/conf.js dafaultLang 'zh' $dafaultLang 
replacePro ${webDir}/app/conf.js isOnlyEn false $isOnlyEn 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

优化后版本:替换匹配的一行;

function getEnvConf() {
  param=$1
  value1=$(sed -E '/^#.*|^ *$/d' $defaultFile | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  value2=$(sed -E '/^#.*|^ *$/d' $targetFile | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  if [ ! -n "$value2" ]; then
    value=$value1
  else
    value=$value2
  fi
  echo $value
}

replacePro() {
  varKey=$2
  varValue=$3
  echo $varKey $varValue
  if [[ "$OSTYPE" == "darwin"* ]]; then
    sed -i "" "s/\(${varKey} =\).*/\1 ${varValue};/" $1
  else
    sed -i "s#^var ${varKey} = ${defaultValue}.*#var ${varKey} = ${varValue};#g" $1
  fi
}

isPro=$(getEnvConf isPro)
defaultLang=$(getEnvConf defaultLang)
isOnlyEn=$(getEnvConf isOnlyEn)

replacePro ${webDir}/app/conf.js isPro $isPro
replacePro ${webDir}/app/conf.js defaultLang $defaultLang
replacePro ${webDir}/app/conf.js isOnlyEn $isOnlyEn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

优化添加判断文件是否存在规则;

#!/bin/bash

curDir=$(
  cd "$(dirname "$0")"
  pwd
)
webDir=${curDir}/web
#defaultFile=$(pwd)/env/.env.default # 得使用绝对路径,而不使用相对路径;
#targetFile=$(pwd)/env/.env.$1
defaultFile=${curDir}/env/.env.default
targetFile=${curDir}/env/.env.$1

function getEnvConf() {
  param=$1
  value1=$(sed -E '/^#.*|^ *$/d' $defaultFile | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  if [ -f $targetFile ]; then
    value2=$(sed -E '/^#.*|^ *$/d' $targetFile | sed s/[[:space:]]//g | awk -F "${param}=" "/${param}=/{print \$2}" | tail -n1)
  fi
  if [ ! -n "$value2" ]; then
    value=$value1
  else
    value=$value2
  fi
  echo $value
}

replacePro() {
  varKey=$2
  varValue=$3
  echo $varKey $varValue
  if [[ "$OSTYPE" == "darwin"* ]]; then
    sed -i "" "s/\(${varKey} =\).*/\1 ${varValue};/" $1
  else
    sed -i "s/\(${varKey} =\).*/\1 ${varValue};/" $1
  fi
}


if [ -n "$1" ]; then
  if [ ! -f $targetFile ]; then
    echo "File env not exist ${targetFile}"
    echo "Please enter a legal ENV configuration!!!"
    exit $E_NO_FILES
  fi
fi

isPro=$(getEnvConf isPro)
defaultLang=$(getEnvConf defaultLang)
isOnlyEn=$(getEnvConf isOnlyEn)

replacePro ${webDir}/app/conf.js isPro $isPro
replacePro ${webDir}/app/conf.js defaultLang $defaultLang
replacePro ${webDir}/app/conf.js isOnlyEn $isOnlyEn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

conf.js

//初始化默认项要跟上面的replacePro参数对应上;
var isPro = false;
var isOnlyEn = false;
var defaultLang = 'zh';
1
2
3
4

# .conf文件读取

大多数的配置文件都是以key=value形式存在的。配置项完全由键值对组成。这样的配置文件读写也是最简单的,假如有以下配置文件user.conf

id=1
name=samy
phone=1234567890
1
2
3

# source

配置的读取很简单,只要将其source进来即可:

samy@samy-HP440:~/project/shell$ cat setup.sh 
#!/bin/bash

source user.conf
echo "id = $id"
echo "name = $name"
echo "phone = $phone"

samy@samy-HP440:~/project/shell$ ./setup.sh 
id = 1
name = samy
phone = 1234567890
samy@samy-HP440:~/project/shell$
1
2
3
4
5
6
7
8
9
10
11
12
13

# sed

上面的方法看似简单,但可能会有问题,shell的赋值语句=号两边是不能有空格的,万一用户不小心多加了空白符呢。为防止这样的情况出现,还是换另一种写法比较安全:

samy@samy-HP440:~/project/shell$ cat user.conf 
id=1
name=samy
phone=1234567890

samy@samy-HP440:~/project/shell$ cat setup.sh 
#!/bin/bash
function get_config() {
    local configPath=$1
    local configName=$2
    sed -n 's/^[[:space:]]*'$configName'[[:space:]]*=[[:space:]]*\(.*[^[:space:]]\)\([[:space:]]*\)$/\1/p' $configPath
}

function set_config() {
    local configPath=$1
    local configName=$2
    local confgValue=$3
    sed -i 's/^[[:space:]]*'$configName'[[:space:]]*=.*/'$configName'='$confgValue'/g' $configPath
}

get_config user.conf name
set_config user.conf name samy
get_config user.conf name

samy@samy-HP440:~/project/shell$ ./setup.sh 
samy
samy

samy@samy-HP440:~/project/shell$ cat user.conf 
id=1
name=samy
phone=1234567890
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# section 配置文件读取

假如配置文件是由多个 section 组成的呢,就像下面这样:

[id=1]
name=samy
phone=1234567890

[id=2]
name=shaojiang
phone=5678901234

[id=3]
name=zhaotong
phone=8901234567
1
2
3
4
5
6
7
8
9
10
11

需要根据输入的id来读写相应的配置项。这样就不能简单的使用前面介绍的方法了。遇到这种情况,可以使用下面这种方法,将配置文件拆分成多个,分别存放到不同的目录中:

config
    -id1
        -user.conf
    -id2
        -user.conf
    -id3
        -user.conf
1
2
3
4
5
6
7

根据id读取不同目录下的配置文件即可。如果配置信息很多的话,推荐使用这种方法,目前在82平台上的机型配置就是使用这种方法来实现的。但若是配置信息很少,且可能有其他脚本也使用到了这个配置文件的时候,拆分配置文件可能就行不通了,需要寻找其他方法。要读写这种格式的配置文件比较复杂,下面是我使用的方法:

samy@samy-HP440:~/project/shell$ cat user.conf 
[id=1]
name=samy
phone=1234567890

[id=2]
name=shaojiang
phone=5678901234

[id=3]
name=zhaotong
phone=8901234567

samy@samy-HP440:~/project/shell$ cat setup.sh 
#!/bin/bash
function string_trim()
{
    echo "$1" | sed 's/^[[:space:]]*\(.*[^[:space:]]\)\([[:space:]]*\)$/\1/g'
}

function get_region() {
    local configPath=$1
    local userID=$2
    cat -n $configPath | grep "\\[id=.*\\]" | grep -A 1 "\\[id=$userID\\]" | awk '{print $1}' | xargs
}

function get_config() {
    local configPath=$1
    local userID=$2
    local configName=$3

    local region=$(get_region $configPath $userID)
    local startLine=$(echo $region | awk '{print $1}')
    local endLine=$(echo $region | awk '{print $2}')
    string_trim $(sed -n "${startLine}, ${endLine} s/\(${configName}.*=.*\)/\1/p" $configPath | awk -F= '{print $2}')
}

function set_config() {
    local configPath=$1
    local userID=$2
    local configName=$3
    local confgValue=$4

    local region=$(get_region $configPath $userID)
    local startLine=$(echo $region | awk '{print $1}')
    local endLine=$(echo $region | awk '{print $2}')
    sed -i "${startLine}, ${endLine} s/${configName}.*=.*/${configName}=${confgValue}/g" $configPath
}

get_config user.conf 2 name
set_config user.conf 2 name samy
get_config user.conf 2 name

samy@samy-HP440:~/project/shell$ ./setup.sh 
shaojiang
samy
samy@samy-HP440:~/project/shell$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

这种方法的思想是先找出指定id的配置所在的区域,即从哪行开始,到哪行结束。只要找到这个区间就好办了,因为sed可以指定只处理的区间。获取区间的方法解释如下:

cat -n $configPath #给每一行加上行号
| grep "\\[id=.*\\]" #打印所有的`id`配置行
| grep -A 1 "\\[id=$userID\\]" #打印匹配的ID行,及下一行,下一行即为下一个配置section的起始行
| awk '{print $1}' | xargs #提取两个行号,即所需section的起始行和下一个配置section的起始行。
1
2
3
4

这种方法当时也是突发其想,想出来的。现在回头看看,其实使用倒推法应该不难想出这种方法。即最后应该是使用sed处理指定区间的文本。那前提就是需要找出section的区间了。而区间也就是两个行号,自然想到要cat -n

# .ini文件读取

# 默认配置

比如在 eaxmple.ini

ftp_url = 127.0.0.1:223
ftp_user = admin
ftp_password = ftp~!@#$%
1
2
3

则在shell脚本中读取配置

FTP_URL = cat eaxmple.ini | grep ftp_url | awk -F'=' '{ print $2 }' | sed s/[[:space:]]//g
FTP_USER = cat eaxmple.ini | grep ftp_user | awk -F'=' '{ print $2 }' | sed s/[[:space:]]//g
FTP_PASSWORD = cat eaxmple.ini | grep ftp_password | awk -F'=' '{ print $2 }' | sed s/[[:space:]]//g
1
2
3

此处必须使用sed s/[[:space:]]//g 去除 多余的空格符; 之前没有使用sed 在自动登录ftp时,会出现总是连不上的问题。

另外附上 ftp下载文件代码 函数

l_user=$1
l_pass=$2
l_host=$3
l_file=$4
lftp << EOF
open ftp://$l_user:$l_pass@$l_host
get $l_file
EOF
1
2
3
4
5
6
7
8

注意:shell脚本和配置文件下载到本地之后可能会有格式问题,所以要首先执行格式命令去除 乱码影响 dos2unix filename

# 参考链接

  • https://www.jianshu.com/p/b48e68c6f1a2
  • https://www.cnblogs.com/kakaisgood/p/8330576.html
  • https://blog.csdn.net/u013105439/article/details/52138007
上次更新: 2022/04/15, 05:41:30
×