Shell脚本读取配置文件
- 一、键值对格式配置文件(最常用)
- 1.1、配置文件示例
- 1.2、source命令导入
- 1.3、sed解析
- 1.4、解析数组
- 二、INI格式配置文件
- 1.1、配置文件示例
- 1.2、sed解析
- 1.3、ini配置带数组(显式声明数组)
- 1.4、ini配置带数组(逗号分隔数组)
- 三、Yaml格式配置文件
- 3.1、配置文件示例
- 3.2、安装yq工具
- 3.3、使用yq工具解析
- 3.4、使用python的PyYAML库
- 四、JSON格式配置文件
- 4.1、配置文件示例
- 4.2、安装jq
- 4.3、jq读取配置脚本
- 4.4、Python的json模块解析
- 五、环境变量文件(.env格式)
- 5.1、配置文件示例
- 5.2、source加载
- 5.3、逐行解析(更安全)
- 5.4、完整案例(生产级实现)
一、键值对格式配置文件(最常用)
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