Linux运维——Shell脚本读取配置文件

一、键值对格式配置文件(最常用)

1.1、配置文件示例

config.cfg

# 这是注释
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASS=password123
DEBUG_MODE=true

1.2、source命令导入

#!/bin/bash
# 直接source导入(简单但需确保配置文件安全)
source config.cfg

# 使用配置
echo "数据库主机: $DB_HOST"
echo "数据库端口: $DB_PORT"

直接使用source加载配置可能存在问题,更好的办法是进行签名或哈希校验再加载:

#!/bin/bash

#检查文件签名或哈希
expected_hash="45626d89c487f717fedfd769bce914f1eb7e73fccb341544aa7645af4b41945d"
actual_hash=$(sha256sum config.cfg | cut -d' ' -f1)

if [[ "$expected_hash" != "$actual_hash" ]]; then
    echo "错误:配置文件哈希不匹配"
    exit 1
fi

source config.cfg

echo $DB_HOST
echo $DB_PORT

1.3、sed解析

#!/bin/bash

# sed解析
while IFS='=' read -r key value; do
    # 跳过注释和空行
    [[ "$key" =~ ^#.*$ ]] || [[ -z "$key" ]] && continue
    
    # 去除可能的空格和引号
    key=$(echo $key | tr -d '[:space:]')
    value=$(echo $value | tr -d '[:space:]' | sed "s/^['\"]//;s/['\"]$//")
    
    # 赋值给变量
    declare "$key=$value"
done < config.cfg

# 使用配置
echo "数据库主机: $DB_HOST"
echo "数据库端口: $DB_PORT"

1.4、解析数组

配置文件示例(config.txt):

# 应用配置
APP_NAME="MyApp"
APP_VERSION=1.0.0

# 逗号分隔的数组
ADMIN_USERS="user1,user2,user3"
ALLOWED_IPS="192.168.1.1,192.168.1.2,10.0.0.1"
FEATURES="auth,logging,dashboard,api"

读取脚本:

#!/bin/bash

# 加载配置文件函数
load_csv_config() {
    local config_file="$1"

    # 检查文件是否存在
    [ -f "$config_file" ] || { echo "错误: 配置文件不存在"; exit 1; }
    
    # 逐行处理配置文件
    while IFS='=' read -r key value; do
        # 跳过注释和空行
        [[ "$key" =~ ^#.*$ ]] || [[ -z "$key" ]] && continue
        
        # 去除键和值的首尾空格
        key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
        value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
        
        # 去除值两端的引号
        value=${value%\"}
        value=${value#\"}
        
        # 检查是否是逗号分隔的值
        if [[ "$value" == *,* ]]; then
            # 临时修改IFS来分割数组
            local IFS=','
            # 读取为数组
            read -ra array <<< "$value"
            # 恢复默认IFS
            unset IFS
            
            # 创建数组变量
            declare -ag "${key}=(${array[*]})"
        else
            # 普通变量赋值
            declare -g "$key=$value"
        fi
    done < "$config_file"
}

# 加载配置文件
load_csv_config "config.txt"

# 使用配置
echo "应用名称: $APP_NAME"
echo "应用版本: $APP_VERSION"

echo -e "\n管理员用户:"
printf "  - %s\n" "${ADMIN_USERS[@]}"

echo -e "\n允许的IP地址:"
printf "  - %s\n" "${ALLOWED_IPS[@]}"

echo -e "\n功能特性:"
echo "${FEATURES[@]}"

二、INI格式配置文件

1.1、配置文件示例

app.ini

[database]
host=127.0.0.1
port=3306
user=root
password=secret

[application]
debug=true
log_level=warning

1.2、sed解析

#!/bin/bash

# 使用awk解析INI文件
parse_ini() {
    local ini_file="$1"
    local section=""
    
    while IFS= read -r line; do
        line=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
        
        # 跳过注释和空行
        [[ -z "$line" ]] || [[ "$line" =~ ^\; ]] || [[ "$line" =~ ^\# ]] && continue
        
        # 处理节头
        if [[ "$line" =~ ^\[(.*)\]$ ]]; then
            section="${BASH_REMATCH[1]}"
        # 处理键值对
        elif [[ "$line" =~ ^([^=]+)=(.*)$ ]]; then
            key="${BASH_REMATCH[1]}"
            value="${BASH_REMATCH[2]}"

            # 去除首尾空格
            key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
            value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
            

            # 创建变量名 SECTION_KEY
            if [[ -n "$section" ]]; then
                var_name="${section}_${key}"
            else
                var_name="$key"
            fi
            
            # 赋值
            declare -g "$var_name=$value"
        fi
    done < "$ini_file"
}

parse_ini "app.ini"

# 使用配置
echo "数据库主机: ${database_host}"
echo "应用日志级别: ${application_log_level}"
echo $application_debug

1.3、ini配置带数组(显式声明数组)

app1.ini

[application]
name = My Application
version = 1.2.3

[arrays]
admin_users[] = admin1
admin_users[] = admin2
admin_users[] = admin3

allowed_ips[] = 192.168.1.1
allowed_ips[] = 192.168.1.2
allowed_ips[] = 10.0.0.1

config_paths[] = /etc/app/config.d
config_paths[] = /var/lib/app/config
config_paths[] = ~/.app/config
#!/bin/bash

# 声明关联数组存储配置
declare -A config
declare -a admin_users allowed_ips config_paths

# 解析INI文件函数
parse_ini() {
    local ini_file="$1"
    local current_section=""

    [ -f "$ini_file" ] || { echo "错误: 文件不存在"; return 1; }

    while IFS= read -r line; do
        # 移除注释和空格
        line="${line%%[;#]*}"  # 移除注释
        line="${line##[[:space:]]}"  # 移除前导空格
        line="${line%%[[:space:]]}"  # 移除尾部空格

        # 跳过空行
        [ -z "$line" ] && continue

        # 处理节(section)
        if [[ "$line" =~ ^\[(.+)\]$ ]]; then
            current_section="${BASH_REMATCH[1]}"
            continue
        fi

        # 处理数组元素 匹配以某些字符开头[]结尾的即key
        if [[ "$line" =~ ^([^[:space:]=]+)\[\] ]]; then
            local array_name="${BASH_REMATCH[1]}"
            local value="${line#*=[[:space:]]}"
            value="${value%%[[:space:]]}"

            # 去除值两端的引号
            value="${value%\"}"
            value="${value#\"}"
            value="${value%\'}"
            value="${value#\'}"

            # 处理波浪线路径扩展
            if [[ "$value" =~ ^~ ]]; then
                value="${value/#\~/$HOME}"
            fi

            # 根据数组名称添加到对应数组
            case "$array_name" in
                "admin_users") admin_users+=("$value") ;;
                "allowed_ips") allowed_ips+=("$value") ;;
                "config_paths") config_paths+=("$value") ;;
            esac
            continue
        fi

        # 处理普通键值对
        if [[ "$line" =~ ^([^[:space:]=]+)[[:space:]]*=[[:space:]]*(.*)$ ]]; then
            local key="${BASH_REMATCH[1]}"
            local value="${BASH_REMATCH[2]}"

            # 去除值两端的引号
            value="${value%\"}"
            value="${value#\"}"
            value="${value%\'}"
            value="${value#\'}"

            # 存储到关联数组
            if [ -n "$current_section" ]; then
                config["${current_section}.${key}"]="$value"
            else
                config["${key}"]="$value"
            fi
        fi
    done < "$ini_file"
}

# 使用示例
parse_ini "app1.ini"

# 访问配置值
echo "应用名称: ${config[application.name]}"
echo "应用版本: ${config[application.version]}"

# 访问数组配置
echo -e "\n管理员用户:"
printf " - %s\n" "${admin_users[@]}"

echo -e "\n允许的IP地址:"
printf " - %s\n" "${allowed_ips[@]}"

echo -e "\n配置路径:"
printf " - %s\n" "${config_paths[@]}"
echo "${config_paths[@]}"

# 验证路径是否存在
echo -e "\n验证配置路径是否存在:"
for path in "${config_paths[@]}"; do
    if [ -e "$path" ]; then
        echo " [存在] $path"
    else
        echo " [不存在] $path"
    fi
done

1.4、ini配置带数组(逗号分隔数组)

配置文件app2.ini

[application]
name = My Application
version = 1.2.3

[arrays]
admin_users = admin1,admin2,admin3

allowed_ips = 192.168.1.1,192.168.1.2,10.0.0.1

config_paths = /etc/app/config.d,/var/lib/app/config,~/.app/config

解析app2.sh

#!/bin/bash

# 声明关联数组存储配置
declare -A config
declare -a admin_users allowed_ips config_paths

# 解析INI文件函数
parse_ini() {
    local ini_file="$1"
    local current_section=""
    
    [ -f "$ini_file" ] || { echo "错误: 文件不存在"; return 1; }
    
    while IFS= read -r line; do
        # 移除注释和空格
        line="${line%%[;#]*}"  # 移除注释
        line="${line##[[:space:]]}"  # 移除前导空格
        line="${line%%[[:space:]]}"  # 移除尾部空格
        
        # 跳过空行
        [ -z "$line" ] && continue
        
        # 处理节(section)
        if [[ "$line" =~ ^\[(.+)\]$ ]]; then
            current_section="${BASH_REMATCH[1]}"
            continue
        fi
        
        # 处理键值对
        if [[ "$line" =~ ^([^[:space:]=]+)[[:space:]]*=[[:space:]]*(.*)$ ]]; then
            local key="${BASH_REMATCH[1]}"
            local value="${BASH_REMATCH[2]}"
            
            # 去除值两端的引号
            value="${value%\"}"
            value="${value#\"}"
            value="${value%\'}"
            value="${value#\'}"
            
            # 处理波浪线路径扩展
            if [[ "$value" =~ ^~ ]]; then
                value="${value/#\~/$HOME}"
            fi
            
            # 存储到关联数组
            if [ -n "$current_section" ]; then
                config["${current_section}.${key}"]="$value"
            else
                config["${key}"]="$value"
            fi
            
            # 处理逗号分隔的数组
            if [[ "$value" == *,* ]]; then
                case "$key" in
                    "admin_users")
                        IFS=',' read -ra admin_users <<< "$value"
                        ;;
                    "allowed_ips")
                        IFS=',' read -ra allowed_ips <<< "$value"
                        ;;
                    "config_paths")
                        IFS=',' read -ra config_paths <<< "$value"
                        # 处理路径中的波浪线
                        for i in "${!config_paths[@]}"; do
                            if [[ "${config_paths[i]}" =~ ^~ ]]; then
                                config_paths[i]="${config_paths[i]/#\~/$HOME}"
                            fi
                        done
                        ;;
                esac
            fi
        fi
    done < "$ini_file"
}

# 使用示例
parse_ini "app2.ini"

# 访问配置值
echo "应用名称: ${config[application.name]}"
echo "应用版本: ${config[application.version]}"

# 访问数组配置
echo -e "\n管理员用户:"
printf " - %s\n" "${admin_users[@]}"

echo -e "\n允许的IP地址:"
printf " - %s\n" "${allowed_ips[@]}"

echo -e "\n配置路径:"
printf " - %s\n" "${config_paths[@]}"

# 验证路径是否存在
echo -e "\n验证配置路径是否存在:"
for path in "${config_paths[@]}"; do
    if [ -e "$path" ]; then
        echo " [存在] $path"
    else
        echo " [不存在] $path"
    fi
done

# 验证IP地址格式
echo -e "\n验证IP地址格式:"
for ip in "${allowed_ips[@]}"; do
    if [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        echo " [有效] $ip"
    else
        echo " [无效] $ip"
    fi
done

三、Yaml格式配置文件

3.1、配置文件示例

config.yaml

application:
  name: My Application
  version: 1.2.3

database:
  host: localhost
  port: 3306
  credentials:
    username: admin
    password: secret123

features:
  - logging
  - auth
  - api

3.2、安装yq工具

yq是一个强大的YAML处理工具,类似于jq但专门用于YAML。

# 使用pip安装
pip install yq

# 或者使用包管理器
# Ubuntu/Debian
sudo apt-get install yq

# CentOS/RHEL
sudo yum install yq

# macOS
brew install yq

3.3、使用yq工具解析

#!/bin/bash

# 读取简单值
app_name=$(yq e '.application.name' config.yaml)
app_version=$(yq e '.application.version' config.yaml)

# 读取嵌套值
db_host=$(yq e '.database.host' config.yaml)
db_port=$(yq e '.database.port' config.yaml)

# 读取数组
features=$(yq e '.features[]' config.yaml)

# 输出结果
echo "应用名称: $app_name"
echo "应用版本: $app_version"
echo "数据库主机: $db_host"
echo "数据库端口: $db_port"
echo "功能特性:"
echo "$features" | while read -r feature; do
  echo " - $feature"
done

3.4、使用python的PyYAML库

如果系统中有Python,可以使用PyYAML库解析YAML。

#!/bin/bash

# 使用Python解析YAML
parse_yaml() {
    python3 -c "
import yaml
import sys

with open('$1') as f:
    config = yaml.safe_load(f)
    
    # 输出为Shell变量格式
    def print_items(d, prefix=''):
        for k, v in d.items():
            if isinstance(v, dict):
                print_items(v, f'{prefix}{k}_')
            else:
                print(f'{prefix}{k}=\"{v}\"')
    
    print_items(config)
"
}

# 加载配置
eval "$(parse_yaml config.yaml)"

# 使用配置
echo "应用名称: $application_name"
echo "应用版本: $application_version"
echo "数据库主机: $database_host"
echo "数据库端口: $database_port"
echo "数据库用户名: $database_credentials_username"

四、JSON格式配置文件

4.1、配置文件示例

config.json

{
  "application": {
    "name": "My App",
    "version": "1.2.3",
    "debug": false
  },
  "database": {
    "host": "localhost",
    "port": 3306,
    "credentials": {
      "username": "admin",
      "password": "secret123"
    }
  },
  "features": ["logging", "auth", "api"]
}

4.2、安装jq

jq是一个强大的命令行JSON处理器,非常适合处理JSON配置文件。

# Ubuntu/Debian
sudo apt-get install jq

# CentOS/RHEL
sudo yum install jq

# macOS
brew install jq

4.3、jq读取配置脚本

#!/bin/bash

# 检查jq是否安装
if ! command -v jq &> /dev/null; then
    echo "错误: 需要安装jq工具"
    exit 1
fi

# 检查配置文件是否存在
if [ ! -f "config.json" ]; then
    echo "错误: 配置文件config.json不存在"
    exit 1
fi

# 读取简单值
app_name=$(jq -r '.application.name' config.json)
app_version=$(jq -r '.application.version' config.json)
debug_mode=$(jq -r '.application.debug' config.json)

# 读取嵌套值
db_host=$(jq -r '.database.host' config.json)
db_port=$(jq -r '.database.port' config.json)
db_user=$(jq -r '.database.credentials.username' config.json)

# 读取数组到Bash数组
mapfile -t features < <(jq -r '.features[]' config.json)

# 输出结果
echo "应用配置:"
echo "  名称: $app_name"
echo "  版本: $app_version"
echo "  调试模式: $debug_mode"
echo "数据库配置:"
echo "  主机: $db_host"
echo "  端口: $db_port"
echo "  用户名: $db_user"
echo "功能特性:"
for feature in "${features[@]}"; do
    echo "  - $feature"
done

4.4、Python的json模块解析

如果系统中有Python,可以使用Python的标准库json来解析JSON文件。

#!/bin/bash

# 使用Python解析JSON
parse_json() {
    python3 -c "
import json
import sys

with open('$1') as f:
    config = json.load(f)
    
    # 输出为Shell变量格式
    def print_items(d, prefix=''):
        for k, v in d.items():
            if isinstance(v, dict):
                print_items(v, f'{prefix}{k}_')
            elif isinstance(v, list):
                print(f'{prefix}{k}=(')
                for item in v:
                    print(f'\"{item}\"')
                print(')')
            else:
                print(f'{prefix}{k}=\"{v}\"')
    
    print_items(config)
"
}

# 加载配置
eval "$(parse_json config.json)"

# 使用配置
echo "应用名称: $application_name"
echo "应用版本: $application_version"
echo "调试模式: $application_debug"
echo "数据库主机: $database_host"
echo "数据库端口: $database_port"
echo "数据库用户名: $database_credentials_username"
echo "功能特性:"
for feature in "${features[@]}"; do
    echo "  - $feature"
done

五、环境变量文件(.env格式)

5.1、配置文件示例

config.env

# 应用配置
APP_NAME="My Application"
APP_VERSION=1.2.3
DEBUG=true

# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=admin
DB_PASS="secret123"

# 路径配置
LOG_DIR=/var/log/myapp
CONFIG_DIR="/etc/myapp/config"

5.2、source加载

#!/bin/bash

# 检查.env文件是否存在
if [ ! -f ".env" ]; then
    echo "错误: .env文件不存在"
    exit 1
fi

# 方法1:直接source导入(注意安全问题)
set -a  # 自动导出所有变量
source .env
set +a  # 关闭自动导出

# 使用配置
echo "应用名称: $APP_NAME"
echo "应用版本: $APP_VERSION"
echo "调试模式: $DEBUG"
echo "数据库主机: $DB_HOST"
echo "数据库端口: $DB_PORT"
echo "日志目录: $LOG_DIR"
echo "配置目录: $CONFIG_DIR"

# 验证路径是否存在
if [ ! -d "$LOG_DIR" ]; then
    echo "警告: 日志目录不存在 - $LOG_DIR"
fi

5.3、逐行解析(更安全)

#!/bin/bash

# 安全的.env文件解析函数
load_dotenv() {
    local env_file="$1"
    
    # 检查文件是否存在且可读
    if [ ! -f "$env_file" ] || [ ! -r "$env_file" ]; then
        echo "错误: 无法访问.env文件"
        return 1
    fi
    
    # 逐行处理.env文件
    while IFS= read -r line; do
        # 跳过注释和空行
        [[ "$line" =~ ^[[:space:]]*# ]] && continue
        [[ -z "$line" ]] && continue
        
        # 处理变量赋值
        if [[ "$line" =~ ^([[:alnum:]_]+)=(.*)$ ]]; then
            local var_name="${BASH_REMATCH[1]}"
            local var_value="${BASH_REMATCH[2]}"
            
            # 去除值两端的引号
            var_value="${var_value%\"}"
            var_value="${var_value#\"}"
            var_value="${var_value%\'}"
            var_value="${var_value#\'}"
            
            # 设置变量
            declare -g "$var_name"="$var_value"
            export "$var_name"
        fi
    done < "$env_file"
}

# 加载.env文件
load_dotenv ".env"

echo "应用名称: $APP_NAME"
echo "应用版本: $APP_VERSION"
echo "调试模式: $DEBUG"
echo "数据库主机: $DB_HOST"
echo "数据库端口: $DB_PORT"
echo "日志目录: $LOG_DIR"
echo "配置目录: $CONFIG_DIR"

# 验证路径是否存在
if [ ! -d "$LOG_DIR" ]; then
    echo "警告: 日志目录不存在 - $LOG_DIR"
fi

5.4、完整案例(生产级实现)

#!/bin/bash

# 安全的.env文件加载函数
load_dotenv_safe() {
    local env_file="$1"
    local line var_name var_value
    
    # 检查文件是否存在
    if [ ! -f "$env_file" ]; then
        echo "错误: .env文件不存在" >&2
        return 1
    fi
    
    # 检查文件权限
    if [ "$(stat -c %a "$env_file")" -gt 600 ]; then
        echo "警告: .env文件权限过于开放" >&2
    fi
    
    # 逐行处理
    while IFS= read -r line; do
        # 跳过注释和空行
        [[ "$line" =~ ^[[:space:]]*# ]] && continue
        [[ -z "${line// }" ]] && continue
        
        # 验证变量名格式
        if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
            var_name="${BASH_REMATCH[1]}"
            var_value="${BASH_REMATCH[2]}"
            
            # 去除值两端的引号
            var_value="${var_value%\"}"
            var_value="${var_value#\"}"
            var_value="${var_value%\'}"
            var_value="${var_value#\'}"
            
            # 设置变量
            declare -g "$var_name"="$var_value"
            export "$var_name"
        else
            echo "警告: 忽略无效的变量赋值 - $line" >&2
        fi
    done < "$env_file"
    
    # 验证必需变量
    local required_vars=("APP_NAME" "DB_HOST" "DB_PORT")
    for var in "${required_vars[@]}"; do
        if [ -z "${!var}" ]; then
            echo "错误: 必需变量 $var 未设置" >&2
            return 1
        fi
    done
}

# 加载.env文件
load_dotenv_safe ".env" || exit 1

# 设置默认值
: ${DEBUG:=false}
: ${LOG_DIR:=/var/log/myapp}

# 使用配置
echo "应用配置:"
echo "  名称: $APP_NAME"
echo "  版本: $APP_VERSION"
echo "  调试模式: $DEBUG"
echo "数据库配置:"
echo "  主机: $DB_HOST"
echo "  端口: $DB_PORT"
echo "  用户: $DB_USER"
echo "路径配置:"
echo "  日志目录: $LOG_DIR"
echo "  配置目录: $CONFIG_DIR"

# 验证路径
if [ ! -d "$LOG_DIR" ]; then
    echo "创建日志目录: $LOG_DIR"
    mkdir -p "$LOG_DIR" || {
        echo "错误: 无法创建日志目录" >&2
        exit 1
    }
fi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值