变量与数据类型:字符串、整数、数组 (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}")注意:
$(( ))只支持整数运算。浮点运算需要用bc、awk或python。
第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 (成功) |
$$ | 当前进程 PID | 12345 |
$! | 最后一个后台进程 PID | 12346 |
第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节:课后练习
- 编写脚本,接收两个数字参数并输出它们的加、减、乘、除(整数)结果
- 创建一个关联数组,存储至少 3 种水果及其价格
- 编写一个函数,使用
local变量,验证局部变量在函数外不可见
相关章节: