Shell 变量详解
一、变量基础概念
1.1 变量的定义和赋值
#!/bin/bash
# 变量定义的基本规则
# 1. 基本赋值(等号两边不能有空格)
name="张三"
age=25
score=98.5
is_student=true
# 2. 引用变量
echo "姓名: $name"
echo "年龄: ${age}" # 推荐使用${}形式
# 3. 不加$符号是赋值,加了是引用
new_name=$name # 引用name的值赋给new_name
new_name=name # 把字符串"name"赋给new_name
# 4. 使用let进行算术赋值
let "count=5+3"
echo "计算: $count"
# 5. 命令替换赋值
current_time=$(date)
file_list=`ls` # 反引号也能用,但不推荐
echo "当前时间: $current_time"1.2 变量命名规则
#!/bin/bash
# 合法的变量名
_variable=1
Variable2=2
VAR_3=3
var4Name=4
VAR_NAME_5=5
_6_var=6
# 不合法的变量名
# 2var=1 # 错误:不能以数字开头
# var-name=2 # 错误:不能包含连字符
# var name=3 # 错误:不能有空格
# 区分大小写
NAME="张三"
name="李四"
echo "NAME: $NAME" # 输出: 张三
echo "name: $name" # 输出: 李四二、变量类型
2.1 字符串变量
#!/bin/bash
# 字符串操作
str1="Hello"
str2="World"
# 1. 字符串拼接
result1=$str1$str2 # HelloWorld
result2="$str1 $str2" # Hello World
result3="${str1}${str2}!" # HelloWorld!
# 2. 字符串长度
string="Hello World"
length=${#string}
echo "字符串长度: $length" # 11
# 3. 字符串截取
str="Hello World"
echo ${str:0:5} # Hello (从0开始,截取5个)
echo ${str:6} # World (从6开始到结尾)
echo ${str: -5} # World (从右边数第5个开始)
echo ${str:6:2} # Wo (从6开始,截取2个)
# 4. 字符串查找和替换
str="apple orange apple banana"
# 替换第一个匹配
echo ${str/apple/pear} # pear orange apple banana
# 替换所有匹配
echo ${str//apple/pear} # pear orange pear banana
# 从开头匹配替换
echo ${str/#apple/pear} # pear orange apple banana
# 从结尾匹配替换
echo ${str/%banana/grape} # apple orange apple grape2.2 数值变量
#!/bin/bash
# 数值运算
# 1. 整数运算
num1=10
num2=3
# 使用 $((...))
echo "加法: $((num1 + num2))" # 13
echo "减法: $((num1 - num2))" # 7
echo "乘法: $((num1 * num2))" # 30
echo "除法: $((num1 / num2))" # 3
echo "取余: $((num1 % num2))" # 1
echo "幂运算: $((num1 ** num2))" # 1000
echo "自增: $((num1++))" # 10
echo "自增后: $num1" # 11
echo "自减: $((--num2))" # 2
# 2. 使用 let
let "result=num1 + num2"
echo "let结果: $result"
# 3. 使用 expr(较老的方式)
result=$(expr $num1 + $num2)
echo "expr结果: $result"
# 4. 浮点数运算(需要bc)
float1=10.5
float2=3.2
echo "浮点加法: $(echo "$float1 + $float2" | bc)"
echo "浮点除法: $(echo "scale=2; $float1 / $float2" | bc)"
# 5. 进制转换
decimal=15
echo "十进制: $decimal"
echo "二进制: $((2#$decimal))" # 错误用法,应使用:
echo "二进制: $(echo "obase=2; $decimal" | bc)"
echo "十六进制: $(echo "obase=16; $decimal" | bc)"2.3 数组变量
#!/bin/bash
# 数组操作
# 1. 定义数组
fruits=("apple" "banana" "orange")
numbers=(1 2 3 4 5)
mixed=("hello" 123 "world" 456.7)
# 2. 访问数组元素
echo "第一个水果: ${fruits[0]}" # apple
echo "第二个水果: ${fruits[1]}" # banana
echo "所有水果: ${fruits[@]}" # apple banana orange
echo "水果个数: ${#fruits[@]}" # 3
echo "数组索引: ${!fruits[@]}" # 0 1 2
# 3. 修改数组
fruits[1]="grape" # 修改第二个元素
fruits+=("peach") # 添加元素
fruits[${#fruits[@]}]="watermelon" # 另一种添加方式
# 4. 删除数组元素
unset fruits[2] # 删除第三个元素
unset fruits # 删除整个数组
# 5. 关联数组(bash 4.0+)
declare -A person
person["name"]="张三"
person["age"]=25
person["city"]="北京"
echo "姓名: ${person["name"]}"
echo "城市: ${person["city"]}"
echo "所有键: ${!person[@]}"
echo "所有值: ${person[@]}"2.4 只读变量
#!/bin/bash
# 只读变量
# 定义只读变量
readonly PI=3.14159
readonly APP_NAME="MyApp"
# 尝试修改会报错
# PI=3.14 # 报错: readonly variable
# APP_NAME="NewApp" # 报错
# 查看只读变量
readonly
# 或
readonly -p
# 函数中的只读变量
my_function() {
local readonly LOCAL_CONST="local constant"
echo "局部常量: $LOCAL_CONST"
# LOCAL_CONST="change" # 这里也会报错
}三、变量作用域
3.1 局部变量 vs 全局变量
#!/bin/bash
# 作用域示例
global_var="我是全局变量"
function test_scope() {
local local_var="我是局部变量"
global_var_in_func="我在函数中修改全局变量"
echo "函数内 - 局部变量: $local_var"
echo "函数内 - 全局变量: $global_var"
echo "函数内 - 新全局变量: $global_var_in_func"
}
# 调用函数
test_scope
echo "函数外 - 局部变量: $local_var" # 空
echo "函数外 - 全局变量: $global_var" # 我是全局变量
echo "函数外 - 新全局变量: $global_var_in_func" # 我在函数中修改全局变量3.2 环境变量
#!/bin/bash
# 环境变量演示
# 1. 设置环境变量
export MY_PATH="/usr/local/bin"
export NAME="张三"
# 2. 子进程可以访问
bash -c 'echo "子进程: PATH包含$MY_PATH"'
bash -c 'echo "子进程: 姓名是$NAME"'
# 3. 只对当前Shell生效
export TEMP_VAR="临时变量"
# 4. 永久设置(添加到~/.bashrc或~/.bash_profile)
# echo 'export MY_VAR="value"' >> ~/.bashrc
# source ~/.bashrc
# 5. 常见环境变量
echo "HOME: $HOME"
echo "USER: $USER"
echo "SHELL: $SHELL"
echo "PATH: $PATH"
echo "PWD: $PWD"
echo "OLDPWD: $OLDPWD"
echo "UID: $UID"
echo "PS1: $PS1"四、特殊变量扩展
4.1 默认值设置
#!/bin/bash
# 变量默认值
# ${var:-default} - 如果var未设置或为空,使用default
unset username
echo "用户名: ${username:-guest}" # guest
username=""
echo "用户名: ${username:-guest}" # guest
username="张三"
echo "用户名: ${username:-guest}" # 张三
# ${var-default} - 只有var未设置时使用default
unset city
echo "城市: ${city-Beijing}" # Beijing
city=""
echo "城市: ${city-Beijing}" # 空
city="上海"
echo "城市: ${city-Beijing}" # 上海
# ${var:=default} - 如果var未设置或为空,设置并返回default
unset count
echo "计数: ${count:=0}" # 0
echo "计数现在是: $count" # 0
# ${var:?message} - 如果var未设置或为空,显示错误信息
# unset required_var
# echo "必填: ${required_var:?变量未设置}" # 报错
# ${var:+alternate} - 如果var已设置且非空,使用alternate
debug="true"
echo "调试模式: ${debug:+开启}" # 开启
debug=""
echo "调试模式: ${debug:+开启}" # 空4.2 字符串操作
#!/bin/bash
# 字符串扩展操作
str="/home/user/documents/file.txt"
# 1. 获取长度
echo "长度: ${#str}"
# 2. 删除匹配
# 从左边删除最短匹配
echo "去掉左边: ${str#*/}" # home/user/documents/file.txt
# 从左边删除最长匹配
echo "去掉左边最长: ${str##*/}" # file.txt
# 从右边删除最短匹配
echo "去掉右边: ${str%.*}" # /home/user/documents/file
# 从右边删除最长匹配
echo "去掉右边最长: ${str%%.*}" # /home/user/documents/file
# 3. 替换
filename="document.bak.txt"
echo "替换第一个: ${filename/.bak/}" # document.txt
echo "替换全部: ${filename//./_}" # document_bak_txt
echo "从开头替换: ${filename/#document/myfile}" # myfile.bak.txt
echo "从结尾替换: ${filename/%txt/text}" # document.bak.text
# 4. 提取子串
path="/usr/local/bin/bash"
echo "偏移提取: ${path:1}" # usr/local/bin/bash
echo "范围提取: ${path:1:3}" # usr
echo "反向提取: ${path: -4}" # bash
echo "范围反向: ${path: -4:2}" # ba
# 5. 大小写转换
text="Hello World"
echo "大写: ${text^^}" # HELLO WORLD
echo "小写: ${text,,}" # hello world
echo "首字母大写: ${text^}" # Hello World
echo "首字母小写: ${text,}" # hello World
echo "单词首字母大写: ${text^^[h,w]}" # Hello World五、高级变量技巧
5.1 间接引用
#!/bin/bash
# 间接变量引用
name="张三"
var_name="name"
# 使用间接引用
echo "直接: $name" # 张三
echo "间接: ${!var_name}" # 张三
# 动态变量名
for i in 1 2 3; do
var_$i="值$i"
done
echo "var_1: $var_1"
echo "var_2: $var_2"
echo "var_3: $var_3"5.2 变量属性
#!/bin/bash
# 变量属性设置
# 声明整数
declare -i number
number=10
number="hello" # 会转换为0
echo "整数: $number"
# 声明只读
declare -r CONSTANT=100
# CONSTANT=200 # 报错
# 声明数组
declare -a array=("a" "b" "c")
declare -A dict=([name]="张三" [age]=25)
# 声明导出(环境变量)
declare -x EXPORTED_VAR="exported"
# 查看变量属性
declare -p number
declare -p CONSTANT
declare -p array
declare -p dict5.3 Here Document
#!/bin/bash
# Here Document 变量赋值
# 多行字符串赋值
sql_query=$(cat << 'SQL'
SELECT *
FROM users
WHERE status = 'active'
ORDER BY created_at DESC
LIMIT 10;
SQL
)
echo "SQL查询:"
echo "$sql_query"
# 生成配置文件
config_content=$(cat << 'CONFIG'
# 应用配置
app_name="MyApp"
version="1.0.0"
# 数据库配置
db_host="localhost"
db_port=3306
db_user="admin"
db_pass="secret"
CONFIG
)
echo "$config_content" > app.conf六、实用示例
6.1 配置文件解析
#!/bin/bash
# 配置文件管理
# 配置文件 config.cfg
cat > config.cfg << 'EOF'
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=admin
DB_PASS=secret123
# 应用配置
APP_NAME=MyApplication
DEBUG_MODE=true
LOG_LEVEL=INFO
MAX_CONNECTIONS=100
EOF
# 读取配置文件的函数
load_config() {
local config_file="$1"
if [ ! -f "$config_file" ]; then
echo "配置文件不存在: $config_file"
return 1
fi
# 读取配置,跳过注释和空行
while IFS='=' read -r key value; do
# 跳过注释行
[[ "$key" =~ ^#.* ]] && continue
# 跳过空行
[ -z "$key" ] && continue
# 去除可能的空格
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# 设置变量
declare -g "$key"="$value"
echo "已加载: $key=$value"
done < "$config_file"
}
# 使用配置
load_config "config.cfg"
echo "数据库主机: $DB_HOST"
echo "应用名称: $APP_NAME"
echo "调试模式: $DEBUG_MODE"6.2 安全的变量使用
#!/bin/bash
# 安全的变量使用实践
# 1. 总是引用变量
file_path="/path with spaces/my file.txt"
# 错误做法
rm $file_path # 会尝试删除三个文件
# 正确做法
rm "$file_path"
# 2. 使用默认值
input_file="${1:-default.txt}"
echo "输入文件: $input_file"
# 3. 检查必需参数
: ${2:?"第二个参数是必需的"}
# 4. 输入验证
read -p "请输入年龄: " age
if [[ ! "$age" =~ ^[0-9]+$ ]]; then
echo "错误: 年龄必须是数字"
exit 1
fi
# 5. 数组安全访问
fruits=("apple" "banana" "orange")
index=5
# 检查边界
if [ $index -lt ${#fruits[@]} ]; then
echo "水果: ${fruits[$index]}"
else
echo "索引越界"
fi
# 6. 使用 local 声明局部变量
process_data() {
local temp_data # 明确声明为局部变量
temp_data="处理中..."
echo "$temp_data"
}6.3 性能优化
#!/bin/bash
# 变量使用性能优化
# 1. 避免不必要的子Shell
# 慢
result=$(echo $(date))
# 快
result=$(date)
# 2. 使用内建命令替代外部命令
# 慢
length=$(echo "$string" | wc -c)
# 快
length=${#string}
# 3. 使用变量缓存结果
# 多次调用相同命令
config_file="/etc/myapp.conf"
if [ -f "$config_file" ]; then
# 缓存内容
config_content=$(<"$config_file")
# 多次使用缓存
echo "行数: $(echo "$config_content" | wc -l)"
echo "包含'server': $(echo "$config_content" | grep -c server)"
fi
# 4. 使用数组避免多次调用
files=(*.txt)
echo "有 ${#files[@]} 个txt文件"
for file in "${files[@]}"; do
echo "处理: $file"
done七、调试和测试
7.1 变量调试
#!/bin/bash
# 变量调试技巧
set -x # 开启调试
# 测试变量
test_var="测试值"
echo "变量值: $test_var"
# 显示变量定义
declare -p test_var
# 使用 bash 的调试功能
echo "使用BASH_VERSION: $BASH_VERSION"
# 测试数组
declare -a test_array=("a" "b" "c")
declare -p test_array
set +x # 关闭调试
# 测试未定义变量
set -u
# echo "$undefined_var" # 会报错
set +u7.2 变量测试框架
#!/bin/bash
# 变量测试框架
# 测试函数
test_variable_operations() {
local test_name="$1"
local expected="$2"
local actual="$3"
if [[ "$actual" == "$expected" ]]; then
echo "✓ $test_name: 通过"
return 0
else
echo "✗ $test_name: 失败"
echo " 期望: '$expected'"
echo " 实际: '$actual'"
return 1
fi
}
# 运行测试
echo "=== 开始变量操作测试 ==="
# 测试1: 字符串截取
str="Hello World"
test_variable_operations "字符串截取" "Hello" "${str:0:5}"
# 测试2: 默认值
unset my_var
test_variable_operations "默认值" "default" "${my_var:-default}"
# 测试3: 字符串替换
filename="test.bak.txt"
test_variable_operations "字符串替换" "test.txt" "${filename/.bak/}"
# 测试4: 数组长度
arr=("a" "b" "c")
test_variable_operations "数组长度" "3" "${#arr[@]}"
echo "=== 测试完成 ==="总结要点
- 变量定义:等号两边无空格,区分大小写
- 变量引用:使用
$符号,推荐${var}形式 - 引号使用:双引号允许变量扩展,单引号禁止
- 作用域:默认全局,函数内用
local声明局部变量 - 数组操作:bash支持索引数组和关联数组
- 字符串操作:bash提供丰富的字符串处理功能
- 默认值:使用
${var:-default}处理未定义变量 - 只读变量:使用
readonly或declare -r声明 - 环境变量:使用
export或declare -x导出 - 调试:使用
set -x调试,declare -p查看变量属性
这些技巧可以帮助您更安全、高效地使用Shell变量。
作者:严锋 创建时间:2023-09-15 21:26
最后编辑:严锋 更新时间:2025-12-25 10:39
最后编辑:严锋 更新时间:2025-12-25 10:39