当前位置: 首页 > article >正文

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

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" ]]; thenecho "错误:配置文件哈希不匹配"exit 1
fisource config.cfgecho $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"# 恢复默认IFSunset IFS# 创建数组变量declare -ag "${key}=(${array[*]})"else# 普通变量赋值declare -g "$key=$value"fidone < "$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; doline=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')# 跳过注释和空行[[ -z "$line" ]] || [[ "$line" =~ ^\; ]] || [[ "$line" =~ ^\# ]] && continue# 处理节头if [[ "$line" =~ ^\[(.*)\]$ ]]; thensection="${BASH_REMATCH[1]}"# 处理键值对elif [[ "$line" =~ ^([^=]+)=(.*)$ ]]; thenkey="${BASH_REMATCH[1]}"value="${BASH_REMATCH[2]}"# 去除首尾空格key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')# 创建变量名 SECTION_KEYif [[ -n "$section" ]]; thenvar_name="${section}_${key}"elsevar_name="$key"fi# 赋值declare -g "$var_name=$value"fidone < "$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[] = admin3allowed_ips[] = 192.168.1.1
allowed_ips[] = 192.168.1.2
allowed_ips[] = 10.0.0.1config_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" =~ ^\[(.+)\]$ ]]; thencurrent_section="${BASH_REMATCH[1]}"continuefi# 处理数组元素 匹配以某些字符开头[]结尾的即keyif [[ "$line" =~ ^([^[:space:]=]+)\[\] ]]; thenlocal array_name="${BASH_REMATCH[1]}"local value="${line#*=[[:space:]]}"value="${value%%[[:space:]]}"# 去除值两端的引号value="${value%\"}"value="${value#\"}"value="${value%\'}"value="${value#\'}"# 处理波浪线路径扩展if [[ "$value" =~ ^~ ]]; thenvalue="${value/#\~/$HOME}"fi# 根据数组名称添加到对应数组case "$array_name" in"admin_users") admin_users+=("$value") ;;"allowed_ips") allowed_ips+=("$value") ;;"config_paths") config_paths+=("$value") ;;esaccontinuefi# 处理普通键值对if [[ "$line" =~ ^([^[:space:]=]+)[[:space:]]*=[[:space:]]*(.*)$ ]]; thenlocal key="${BASH_REMATCH[1]}"local value="${BASH_REMATCH[2]}"# 去除值两端的引号value="${value%\"}"value="${value#\"}"value="${value%\'}"value="${value#\'}"# 存储到关联数组if [ -n "$current_section" ]; thenconfig["${current_section}.${key}"]="$value"elseconfig["${key}"]="$value"fifidone < "$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[@]}"; doif [ -e "$path" ]; thenecho " [存在] $path"elseecho " [不存在] $path"fi
done

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

配置文件app2.ini

[application]
name = My Application
version = 1.2.3[arrays]
admin_users = admin1,admin2,admin3allowed_ips = 192.168.1.1,192.168.1.2,10.0.0.1config_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" =~ ^\[(.+)\]$ ]]; thencurrent_section="${BASH_REMATCH[1]}"continuefi# 处理键值对if [[ "$line" =~ ^([^[:space:]=]+)[[:space:]]*=[[:space:]]*(.*)$ ]]; thenlocal key="${BASH_REMATCH[1]}"local value="${BASH_REMATCH[2]}"# 去除值两端的引号value="${value%\"}"value="${value#\"}"value="${value%\'}"value="${value#\'}"# 处理波浪线路径扩展if [[ "$value" =~ ^~ ]]; thenvalue="${value/#\~/$HOME}"fi# 存储到关联数组if [ -n "$current_section" ]; thenconfig["${current_section}.${key}"]="$value"elseconfig["${key}"]="$value"fi# 处理逗号分隔的数组if [[ "$value" == *,* ]]; thencase "$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[@]}"; doif [[ "${config_paths[i]}" =~ ^~ ]]; thenconfig_paths[i]="${config_paths[i]/#\~/$HOME}"fidone;;esacfifidone < "$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[@]}"; doif [ -e "$path" ]; thenecho " [存在] $path"elseecho " [不存在] $path"fi
done# 验证IP地址格式
echo -e "\n验证IP地址格式:"
for ip in "${allowed_ips[@]}"; doif [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; thenecho " [有效] $ip"elseecho " [无效] $ip"fi
done

三、Yaml格式配置文件

3.1、配置文件示例

config.yaml

application:name: My Applicationversion: 1.2.3database:host: localhostport: 3306credentials:username: adminpassword: secret123features:- 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; doecho " - $feature"
done

3.4、使用python的PyYAML库

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

#!/bin/bash# 使用Python解析YAML
parse_yaml() {python3 -c "
import yaml
import syswith 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; thenecho "错误: 需要安装jq工具"exit 1
fi# 检查配置文件是否存在
if [ ! -f "config.json" ]; thenecho "错误: 配置文件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[@]}"; doecho "  - $feature"
done

4.4、Python的json模块解析

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

#!/bin/bash# 使用Python解析JSON
parse_json() {python3 -c "
import json
import syswith 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[@]}"; doecho "  - $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" ]; thenecho "错误: .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" ]; thenecho "警告: 日志目录不存在 - $LOG_DIR"
fi

5.3、逐行解析(更安全)

#!/bin/bash# 安全的.env文件解析函数
load_dotenv() {local env_file="$1"# 检查文件是否存在且可读if [ ! -f "$env_file" ] || [ ! -r "$env_file" ]; thenecho "错误: 无法访问.env文件"return 1fi# 逐行处理.env文件while IFS= read -r line; do# 跳过注释和空行[[ "$line" =~ ^[[:space:]]*# ]] && continue[[ -z "$line" ]] && continue# 处理变量赋值if [[ "$line" =~ ^([[:alnum:]_]+)=(.*)$ ]]; thenlocal 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"fidone < "$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" ]; thenecho "警告: 日志目录不存在 - $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" ]; thenecho "错误: .env文件不存在" >&2return 1fi# 检查文件权限if [ "$(stat -c %a "$env_file")" -gt 600 ]; thenecho "警告: .env文件权限过于开放" >&2fi# 逐行处理while IFS= read -r line; do# 跳过注释和空行[[ "$line" =~ ^[[:space:]]*# ]] && continue[[ -z "${line// }" ]] && continue# 验证变量名格式if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; thenvar_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"elseecho "警告: 忽略无效的变量赋值 - $line" >&2fidone < "$env_file"# 验证必需变量local required_vars=("APP_NAME" "DB_HOST" "DB_PORT")for var in "${required_vars[@]}"; doif [ -z "${!var}" ]; thenecho "错误: 必需变量 $var 未设置" >&2return 1fidone
}# 加载.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" ]; thenecho "创建日志目录: $LOG_DIR"mkdir -p "$LOG_DIR" || {echo "错误: 无法创建日志目录" >&2exit 1}
fi
http://www.lryc.cn/news/2379402.html

相关文章:

  • 答题pk小程序道具卡的获取与应用
  • leetcode3265. 统计近似相等数对 I-medium
  • 【架构篇】代码组织结构设计
  • 2_Spring【IOC容器中获取组件Bean】
  • 日期数据渲染转换问题
  • Spring Boot拦截器详解:原理、实现与应用场景
  • ubuntu18.04编译qt5.14.2源码
  • 创建指定版本的vite项目
  • iOS 初识RunLoop
  • 电子电路仿真实验教学平台重磅上线!——深圳航天科技创新研究院倾力打造,助力高校教学数字化转型
  • 搭建一个WordPress网站需要多少成本
  • Python数据可视化 - Pyecharts绘图示例
  • NC016NC017美光固态芯片NC101NC102
  • [Android] 青木扫描全能文档3.0,支持自动扫描功能
  • Vue 3 动态 ref 的使用方式(表格)
  • Bash fork 炸弹 —— :(){ :|: };:
  • 互联网大厂Java面试:从Spring Boot到微服务架构的技术深挖
  • IT审计之外包
  • 精益数据分析(66/126):技术驱动的大规模用户调研——从工具组合到高效验证
  • 通俗解释Transformer在处理序列问题高效的原因(个人理解)
  • 第12章 Java多线程机制
  • 区间带边权并查集,XY4060泄露的测试点
  • 【数据结构】1-4算法的空间复杂度
  • nt!ExRemoveHeadNBQueue 函数分析
  • OpenAI推出Codex — ChatGPT内置的软件工程Agents
  • AI日报 · 2025年5月15日|GPT-4.1 登陆 ChatGPT
  • W5500使用ioLibrary库创建TCP客户端
  • SQL练习(12/81)
  • 组态王|如何创建组态王工程?
  • mysql数据库-3(备份和恢复)