变量与数据类型:字符串、整数、数组 (Variables & Data Types)

章节概述

Bash 是无类型语言——所有变量本质上都是字符串。本章系统讲解变量的声明与赋值、字符串操作、整数算术运算、数组的使用,以及变量作用域(local)。通过与 C 语言的类型系统对比,建立正确的认知。

核心理念:在 Bash 中,“类型”由上下文决定。同一个变量可以是字符串、整数或数组,取决于你怎么使用它。


第1节:变量声明与赋值

# 基本赋值(等号两边不能有空格!)
name="Bash"
version=5
 
# 引号规则
name="Hello World"      # 双引号:允许变量替换
name='Hello World'      # 单引号:原样输出
name=Hello\ World       # 转义空格
 
# 查看变量值
echo $name
echo ${name}            # 推荐用花括号,避免歧义

常见错误:

# ✗ 错误:等号两边有空格
name = "Bash"    # 报错: command not found
 
# ✗ 错误:变量名以数字开头
1name="test"     # 报错
 
# ✓ 正确
name="Bash"
_name="test"
NAME="test"      # 约定:全大写表示环境变量

C 语言对比:

// C 是强类型,必须声明类型
char name[] = "Bash";    // 字符串
int version = 5;         // 整数
float pi = 3.14;         // 浮点数
 
// Bash 等效(但都是字符串)
# name="Bash"
# version=5
# pi=3.14

第2节:字符串详解

# 字符串长度
str="Hello, Bash!"
echo ${#str}            # 输出: 12
 
# 子串提取
str="Hello, Bash!"
echo ${str:0:5}         # 输出: Hello (从位置0取5个字符)
echo ${str:7}           # 输出: Bash! (从位置7到末尾)
 
# 字符串替换
str="hello world world"
echo ${str/world/Shell}     # 输出: hello Shell world (替换第一个)
echo ${str//world/Shell}    # 输出: hello Shell Shell (替换所有)
 
# 大小写转换 (Bash 4+)
str="Hello"
echo ${str^^}           # 输出: HELLO (全大写)
echo ${str,,}           # 输出: hello (全小写)

字符串操作速查表:

操作语法示例结果
长度${#var}${#str}字符数
子串${var:pos:len}${str:0:5}前5个字符
删除前缀${var#pattern}${str#He*}llo, Bash!
删除后缀${var%pattern}${str%!}Hello, Bash
替换${var/old/new}${str/Bash/Shell}Hello, Shell!

第3节:整数算术运算 $(( ))

a=10
b=3
 
# 基本运算
echo $((a + b))     # 13
echo $((a - b))     # 7
echo $((a * b))     # 30
echo $((a / b))     # 3 (整数除法,截断)
echo $((a % b))     # 1 (取模)
echo $((a ** 2))    # 100 (幂运算)
 
# 自增/自减
((a++))
echo $a             # 11
((a--))
echo $a             # 10
 
# 赋值运算
((a += 5))
echo $a             # 15

整数运算的多种写法:

a=10; b=3
 
# 方式1: $(())
result=$((a + b))
 
# 方式2: let
let "result = a + b"
 
# 方式3: expr (较老,需转义)
result=$(expr $a + $b)
 
# 方式4: awk (浮点运算)
result=$(awk "BEGIN {print $a / $b}")

注意$(( )) 只支持整数运算。浮点运算需要用 bcawkpython


第4节:数组

# 索引数组
fruits=("apple" "banana" "cherry" "date")
 
# 访问元素
echo ${fruits[0]}       # apple (从0开始)
echo ${fruits[2]}       # cherry
 
# 数组长度
echo ${#fruits[@]}      # 4
 
# 遍历数组
for fruit in "${fruits[@]}"; do
    echo "$fruit"
done
 
# 添加元素
fruits+=("elderberry")
 
# 删除元素
unset fruits[1]         # 删除 banana

数组操作速查:

操作语法说明
声明arr=(a b c)用空格分隔
访问${arr[i]}索引从0开始
全部${arr[@]}所有元素
长度${#arr[@]}元素个数
追加arr+=(x)末尾添加
删除unset arr[i]删除指定元素
切片${arr[@]:1:2}从索引1取2个

C 数组对比:

// C 数组:固定类型、固定大小
int nums[5] = {10, 20, 30, 40, 50};
char *fruits[] = {"apple", "banana", "cherry"};
 
printf("%d\n", nums[0]);     // 10
printf("%s\n", fruits[1]);   // banana
# Bash 数组:动态、无类型
nums=(10 20 30 40 50)
fruits=("apple" "banana" "cherry")
 
echo ${nums[0]}          # 10
echo ${fruits[1]}        # banana
# Bash 数组可以混合类型
mixed=("hello" 42 3.14)

第5节:变量作用域(local)

global_var="I am global"
 
my_function() {
    local local_var="I am local"
    func_var="I am function global"
 
    echo "函数内: $local_var"
    echo "函数内: $func_var"
}
 
my_function
echo "函数外: $global_var"   # 可访问
echo "函数外: $func_var"     # 可访问(函数内未用 local)
echo "函数外: $local_var"    # 空(local 限制了作用域)

作用域对比:

变量类型函数内定义函数外可访问子进程可继承
全局变量var="val"否(非 export)
local 变量local var="val"
环境变量export var="val"

最佳实践:函数内始终使用 local 声明变量,避免意外污染全局作用域。


第6节:特殊变量

#!/usr/bin/env bash
echo "脚本名: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "参数个数: $#"
echo "所有参数: $@"
echo "所有参数: $*"
echo "当前进程ID: $$"
echo "上一个命令退出码: $?"

特殊变量速查:

变量含义示例
$0脚本名称./script.sh
$1~$9位置参数./script.sh arg1 arg2
$#参数个数2
$@所有参数(分别引用)"arg1" "arg2"
$*所有参数(作为整体)"arg1 arg2"
$?上一命令退出码0 (成功)
$$当前进程 PID12345
$!最后一个后台进程 PID12346

第7节:readonly 与 unset

# 只读变量
readonly PI=3.14159
PI=3.14    # 报错: readonly variable
 
# 删除变量
temp="temporary"
unset temp
echo $temp    # 空
 
# readonly 不能 unset
readonly CONST="immutable"
unset CONST  # 报错: cannot unset readonly variable

第8节:课后练习

  1. 编写脚本,接收两个数字参数并输出它们的加、减、乘、除(整数)结果
  2. 创建一个关联数组,存储至少 3 种水果及其价格
  3. 编写一个函数,使用 local 变量,验证局部变量在函数外不可见

相关章节: