Linux readonly命令

什么是readonly命令?

readonly命令是Bash shell的内置命令,用于将变量或函数设置为只读(readonly)状态。只读变量和函数不能被修改、重新赋值或删除,这样可以保护重要的配置和数据不被意外修改。

注意: readonly是Bash shell的内置命令,不是外部程序。这意味着它只存在于Bash和兼容的shell中,用于当前shell会话。
用途: readonly常用于脚本中保护重要的配置变量、常量、路径等,确保它们在脚本执行过程中不会被意外修改。

语法格式

# 设置变量为只读 readonly 变量名[=值] # 设置函数为只读 readonly -f 函数名 # 查看所有只读变量 readonly [-p] # 查看所有只读函数 readonly -f

常用选项

选项 描述 示例
(无选项) 设置变量为只读 readonly VAR
-p 列出所有只读变量(默认行为) readonly -p
-f 操作函数而非变量 readonly -f func_name
-a 操作索引数组(Bash 4.3+) readonly -a array
-A 操作关联数组(Bash 4.3+) readonly -A array

基本用法

1. 设置变量为只读:

# 先定义变量,然后设置为只读 VERSION="1.0.0" readonly VERSION # 尝试修改只读变量 VERSION="2.0.0" # 错误:bash: VERSION: readonly variable

2. 定义时直接设置为只读:

# 在定义时直接设置为只读 readonly PI=3.14159265358979 readonly APP_NAME="MyApplication" # 尝试删除只读变量 unset PI # 错误:bash: unset: PI: cannot unset: readonly variable

3. 查看只读变量:

# 列出所有只读变量 readonly # 或 readonly -p # 输出示例: # readonly PI='3.14159265358979' # readonly APP_NAME='MyApplication' # readonly VERSION='1.0.0'

设置函数为只读

1. 设置函数为只读:

# 定义函数 hello() { echo "Hello, World!" } # 设置为只读 readonly -f hello # 尝试重新定义函数 hello() { echo "Goodbye, World!" } # 错误:bash: hello: readonly function

2. 查看只读函数:

# 列出所有只读函数 readonly -f # 输出示例: # hello () # { # echo "Hello, World!" # }

3. 定义时直接设置为只读函数:

# Bash 4.4+ 支持在函数定义时设置为只读 hello() { echo "Hello, World!" } readonly -f hello

设置数组为只读(Bash 4.3+)

1. 设置索引数组为只读:

# 定义索引数组 colors=("red" "green" "blue") # 设置为只读 readonly -a colors # 尝试修改数组元素 colors[0]="yellow" # 错误:bash: colors: readonly variable # 尝试添加元素 colors[3]="yellow" # 错误:bash: colors: readonly variable

2. 设置关联数组为只读:

# 定义关联数组 declare -A config config["host"]="localhost" config["port"]="8080" # 设置为只读 readonly -A config # 尝试修改 config["host"]="127.0.0.1" # 错误:bash: config: readonly variable
注意: 数组的只读特性在Bash 4.3及以上版本中可用。在旧版本中,数组元素可能仍然可以被修改。

实际应用场景

1. 保护配置常量:

#!/bin/bash # 脚本配置常量 readonly CONFIG_FILE="/etc/app/config.conf" readonly LOG_FILE="/var/log/app.log" readonly BACKUP_DIR="/backup/app" # 使用常量 if [[ -f "$CONFIG_FILE" ]]; then source "$CONFIG_FILE" fi # 这些常量在脚本中不会被意外修改 echo "Config file: $CONFIG_FILE"

2. 保护数学常量:

#!/bin/bash # 数学常量 readonly PI=3.14159265358979323846 readonly E=2.71828182845904523536 readonly GOLDEN_RATIO=1.6180339887498948482 # 计算圆的面积 calculate_circle_area() { local radius=$1 echo "scale=2; $PI * $radius * $radius" | bc } area=$(calculate_circle_area 5) echo "Circle area: $area"

3. 保护系统路径:

#!/bin/bash # 系统路径常量 readonly BIN_DIR="/usr/local/bin" readonly LIB_DIR="/usr/local/lib" readonly ETC_DIR="/etc" readonly VAR_DIR="/var" # 确保使用正确的路径 install_binary() { local binary="$1" cp "$binary" "$BIN_DIR/" chmod +x "$BIN_DIR/$(basename "$binary")" } # 路径不会被意外修改 install_binary "./myapp"

4. 保护重要函数:

#!/bin/bash # 重要的工具函数 log_message() { local level="$1" local message="$2" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" >> "$LOG_FILE" } # 设置为只读,防止被重写 readonly -f log_message # 使用只读函数 log_message "INFO" "Application started" # 尝试重写函数会失败 log_message() { echo "Overridden"; } # 错误:bash: log_message: readonly function

在脚本中的使用模式

1. 脚本头部定义只读常量:

#!/bin/bash # 脚本配置部分 readonly SCRIPT_NAME=$(basename "$0") readonly SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) readonly VERSION="1.0.0" readonly AUTHOR="Your Name" # 颜色常量(用于输出) readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly BLUE='\033[0;34m' readonly NC='\033[0m' # No Color echo -e "${GREEN}$SCRIPT_NAME v$VERSION${NC}"

2. 配置文件常量:

#!/bin/bash # 从配置文件加载,然后设置为只读 source config.sh # 将所有配置变量设置为只读 readonly DB_HOST DB_PORT DB_USER DB_PASS readonly API_KEY API_SECRET readonly LOG_LEVEL LOG_FILE # 现在配置无法被修改 echo "Database host: $DB_HOST"

3. 只读参数验证:

#!/bin/bash # 验证和设置只读参数 validate_and_set_params() { local input_file="$1" local output_dir="$2" # 验证输入文件存在 if [[ ! -f "$input_file" ]]; then echo "Error: Input file not found: $input_file" >&2 exit 1 fi # 验证输出目录存在 if [[ ! -d "$output_dir" ]]; then echo "Error: Output directory not found: $output_dir" >&2 exit 1 fi # 设置为只读变量 readonly INPUT_FILE="$input_file" readonly OUTPUT_DIR="$output_dir" } # 使用函数设置只读参数 validate_and_set_params "$1" "$2" # 现在参数是只读的,无法被修改 echo "Processing: $INPUT_FILE -> $OUTPUT_DIR"

常见问题

只读属性在Bash中无法取消! 这是readonly命令的一个重要特性:一旦变量或函数被设置为只读,在当前shell会话中就不能取消这个属性。

解决方法:
1. 在新的shell进程中工作:
# 启动新的bash进程 bash # 在新的shell中,变量不是只读的
2. 重新启动shell会话
3. 在脚本中避免不必要的只读设置
4. 使用子shell隔离修改:
# 在子shell中修改变量 ( # 这里可以重新定义变量 VAR="new value" echo "In subshell: $VAR" )

只读属性会传递给子shell! 这是Bash的默认行为。

# 在父shell中设置只读变量 readonly MY_VAR="parent_value" echo "Parent: $MY_VAR" # 在子shell中尝试修改 ( echo "Subshell before: $MY_VAR" MY_VAR="subshell_value" # 这会失败! echo "Subshell after: $MY_VAR" )
但是,可以使用bash命令启动完全新的shell进程,不会继承只读属性:
# 启动全新的bash进程 bash -c 'MY_VAR="new_value"; echo "New shell: $MY_VAR"'

readonly 和 declare -r 功能相同! 它们都是Bash中设置只读变量的方法。

readonly:
• 是Bash的内置命令
• 语法更简洁
• 主要用于交互式使用

declare -r:
• 也是Bash内置命令
• 是declare命令的一个选项
• 可以同时设置其他属性
• 更适合在复杂的变量声明中使用

示例:
# 两者功能相同 readonly PI=3.14 declare -r PI=3.14 # declare可以同时设置其他属性 declare -ir MAX_CONNECTIONS=100 # 整数且只读 declare -ar COLORS=("red" "green" "blue") # 数组且只读

高级用法

1. 保护整个环境:

#!/bin/bash # 在脚本开头保护重要环境变量 readonly PATH readonly LD_LIBRARY_PATH readonly PYTHONPATH # 防止脚本修改系统路径 echo "PATH is protected: $PATH"

2. 函数库保护:

# library.sh # 定义工具函数 log_info() { echo "[INFO] $1"; } log_error() { echo "[ERROR] $1"; } calculate() { echo "$1" | bc; } # 将所有函数设置为只读 readonly -f log_info log_error calculate

3. 只读变量的导出:

#!/bin/bash # 创建只读变量并导出 readonly CONFIG_PATH="/etc/app/config" export CONFIG_PATH # 在子进程中仍然是只读的 bash -c 'echo "Config: $CONFIG_PATH"; CONFIG_PATH="/tmp"' # 错误:bash: CONFIG_PATH: readonly variable

最佳实践

1. 脚本中使用只读常量:

#!/bin/bash # 最佳实践:在脚本开头定义只读常量 readonly SCRIPT_NAME=$(basename "$0") readonly SCRIPT_VERSION="1.0.0" readonly DEFAULT_TIMEOUT=30 readonly MAX_RETRIES=3 readonly LOG_DIR="/var/log/$(basename "$0" .sh)" # 使用常量 echo "Starting $SCRIPT_NAME v$SCRIPT_VERSION"

2. 保护配置变量:

#!/bin/bash # 加载配置后立即设置为只读 if [[ -f "config.sh" ]]; then source "config.sh" # 将所有配置变量设为只读 readonly $(grep -E '^[A-Z_]+=' config.sh | cut -d'=' -f1 | xargs) fi

3. 谨慎使用只读:

#!/bin/bash # 只在真正需要保护的地方使用readonly # 不要过度使用,否则会限制脚本的灵活性 # 应该设置为只读的: # - 数学常量(PI, E等) # - 配置常量 # - 路径常量 # - 重要的工具函数 # 不应该设置为只读的: # - 循环变量 # - 临时变量 # - 需要修改的变量
重要警告:
1. 只读属性在当前shell会话中无法取消
2. 只读变量会传递给子shell
3. 在交互式shell中谨慎使用readonly
4. 测试脚本时,只读变量可能会影响调试
5. 考虑使用local变量代替只读变量,如果只需要在函数中保护

相关命令

命令 描述 与readonly的区别
declare -r 设置只读变量 功能相同,语法不同,declare可以设置更多属性
typeset -r 设置只读变量(兼容Korn shell) 在Bash中,typeset是declare的别名
local 设置局部变量 只在函数内有效,但不一定是只读的
export 导出变量到子进程 可以导出的变量也可以是只读的
unset 删除变量 不能删除只读变量

常用示例总结

用途 命令 说明
设置只读变量 readonly VAR=value 定义并设置只读变量
设置已存在的变量为只读 readonly VAR 将现有变量设为只读
设置只读函数 readonly -f func_name 保护函数不被重定义
列出只读变量 readonly -p 显示所有只读变量及其值
列出只读函数 readonly -f 显示所有只读函数的定义
设置只读数组 readonly -a array_name 设置索引数组为只读(Bash 4.3+)
设置只读关联数组 readonly -A array_name 设置关联数组为只读(Bash 4.3+)
在脚本中保护配置 readonly $(compgen -v | grep '^[A-Z_]') 将所有大写变量设为只读(谨慎使用)