Linux命令-readonly(Bash 内建设置只读变量)
快速参考 :readonly 是 Bash 内建命令,用于将 shell 变量或函数标记为只读属性,设置为只读后变量不能被修改或取消,在脚本中用于保护关键常量,防止意外覆盖。
提示 :
readonly定义的变量在当前 shell 会话中永久生效,子进程不会继承只读属性。退出会话或其他脚本中不受影响。
命令语法
bash
readonly [选项] [变量名[=值]] ...
readonly -f [函数名 ...]
readonly -p # 显示所有只读变量
常用选项
| 选项 | 说明 |
|---|---|
-p |
显示当前 Shell 中所有只读变量 |
-f |
将指定的函数设置为只读 |
-a |
引用索引数组(配合 -p) |
-A |
引用关联数组(配合 -p) |
场景化实例
1. 基本使用:设置只读变量
bash
#!/bin/bash
# 设置常量
readonly APP_NAME="MyApplication"
readonly APP_VERSION="3.2.1"
echo "应用: $APP_NAME v$APP_VERSION"
# 尝试修改会报错
APP_NAME="NewName" # bash: APP_NAME: readonly variable
2. 声明并赋值同时进行
bash
#!/bin/bash
# 两种方式等价
readonly PI=3.14159
readonly MAX_CONNECTIONS=100
declare -r MIN_THRESHOLD=10
echo "PI=$PI MAX=$MAX_CONNECTIONS MIN=$MIN_THRESHOLD"
# 后续代码中无法修改
# PI=3.14 # 报错!
3. 查看系统已定义的只读变量
bash
#!/bin/bash
# 查看所有只读变量
echo "===== 系统只读变量 ====="
readonly -p
# 过滤特定只读变量
echo "===== 包含 EUID 的变量 ====="
readonly -p | grep EUID
# 输出示例:
# declare -ir BASHPID
# declare -ir EUID="500"
# declare -ir PPID="12345"
# declare -r BASH_COMPLETION_VERSINFO="2.12"
4. 保护函数不被重新定义
bash
#!/bin/bash
# 定义关键函数并设为只读
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2
}
readonly -f log_error
# 尝试重定义
log_error() { echo "hijacked!"; }
# bash: log_error: readonly function
5. 脚本中使用只读变量防止错误
bash
#!/bin/bash
# 配置管理脚本
CONFIG_FILE="/etc/myapp/config.yaml"
readonly CONFIG_FILE
readonly BACKUP_DIR="/var/backup/myapp"
readonly LOG_DIR="/var/log/myapp"
# 保护备份脚本不被注入
backup_config() {
local timestamp=$(date +%Y%m%d_%H%M%S)
cp "$CONFIG_FILE" "$BACKUP_DIR/config_$timestamp.yaml"
echo "备份完成: $BACKUP_DIR/config_$timestamp.yaml"
}
readonly -f backup_config
6. 检测变量是否已设置为只读
bash
#!/bin/bash
# 检测只读属性
is_readonly() {
local var_name="$1"
readonly -p | grep -q "declare -.*r.* $var_name="
return $?
}
export MY_EXPORT_VAR="hello"
readonly MY_READONLY_VAR="world"
is_readonly MY_EXPORT_VAR && echo "MY_EXPORT_VAR 只读" || echo "MY_EXPORT_VAR 可写"
is_readonly MY_READONLY_VAR && echo "MY_READONLY_VAR 只读" || echo "MY_READONLY_VAR 可写"
# 输出:MY_EXPORT_VAR 可写
# MY_READONLY_VAR 只读
7. 数组只读
bash
#!/bin/bash
# 设置只读数组
ALLOWED_HOSTS=("192.168.1.100" "192.168.1.101" "10.0.0.1")
readonly -a ALLOWED_HOSTS
echo "允许的主机:"
for host in "${ALLOWED_HOSTS[@]}"; do
echo " - $host"
done
# ALLOWED_HOSTS+=("192.168.1.102") # 报错!
# 关联数组
declare -A PORT_MAP=([http]=80 [https]=443 [ssh]=22)
readonly -A PORT_MAP
echo "HTTPS 端口: ${PORT_MAP[https]}"
8. 实际脚本:环境配置保护
bash
#!/bin/bash
# 生产环境部署脚本
set -euo pipefail
# 定义只读常量
readonly DEPLOY_ROOT="/opt/application"
readonly RELEASE_DIR="$DEPLOY_ROOT/releases"
readonly CURRENT_LINK="$DEPLOY_ROOT/current"
readonly MAX_RELEASES=5
deploy() {
local version="$1"
echo "部署版本: $version"
mkdir -p "$RELEASE_DIR"
local release_path="$RELEASE_DIR/$version"
mkdir -p "$release_path"
# 创建当前版本的符号链接
ln -sfn "$release_path" "$CURRENT_LINK"
echo "部署完成: $CURRENT_LINK -> $release_path"
# 清理旧版本
cleanup_old_releases
}
cleanup_old_releases() {
local count=$(ls -1 "$RELEASE_DIR" | wc -l)
if [ "$count" -gt "$MAX_RELEASES" ]; then
echo "清理旧版本..."
ls -1t "$RELEASE_DIR" | tail -n +$((MAX_RELEASES + 1)) | while read dir; do
rm -rf "$RELEASE_DIR/$dir"
echo " 已删除: $dir"
done
fi
}
readonly -f deploy cleanup_old_releases
deploy "v2.5.0"
常用系统只读变量
bash
# Bash 内置只读变量
echo "UID=$UID EID=$EUID"
echo "PPID=$PPID BASHPID=$BASHPID"
echo "BASH_VERSION=$BASH_VERSION"
# 输出示例:
# UID=1000 EID=1000
# PPID=4521 BASHPID=5823
# BASH_VERSION=5.1.16(1)-release
最佳实践
常量用大写命名 ,集中在脚本开头定义;关键函数加
readonly -f防止注入。只读变量需在子 shell 中重新声明。
bash
#!/bin/bash
# 推荐模式:脚本开头集中定义
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly TIMESTAMP="$(date +%Y%m%d_%H%M%S)"