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 grape

2.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 dict

5.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 +u

7.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 "=== 测试完成 ==="

总结要点

  1. 变量定义:等号两边无空格,区分大小写
  2. 变量引用:使用$符号,推荐${var}形式
  3. 引号使用:双引号允许变量扩展,单引号禁止
  4. 作用域:默认全局,函数内用local声明局部变量
  5. 数组操作:bash支持索引数组和关联数组
  6. 字符串操作:bash提供丰富的字符串处理功能
  7. 默认值:使用${var:-default}处理未定义变量
  8. 只读变量:使用readonlydeclare -r声明
  9. 环境变量:使用exportdeclare -x导出
  10. 调试:使用set -x调试,declare -p查看变量属性

这些技巧可以帮助您更安全、高效地使用Shell变量。

作者:严锋  创建时间:2023-09-15 21:26
最后编辑:严锋  更新时间:2025-12-25 10:39