第53章:Bash 脚本基础

第五十三章:Bash 脚本基础

53.1 什么是 Shell 脚本?

想象一下:你每天上班都要做一堆重复的事情——打开电脑、登录微信、打开邮箱、泡杯咖啡(这不是重复,这是仪式感)、检查任务列表……

如果每天都要手动做这些事情,你会想:“有没有一种方法,让我告诉电脑’每天早上自动帮我做这些’?”

答案是:有的,Shell 脚本就是你的私人秘书!

53.2 什么是 Shell?

在讲脚本之前,我们先认识一下 Shell

Shell 是 Linux 的命令行解释器,你可以把它理解为"人和 Linux 内核之间的翻译官":

graph LR
    A[你] -->|输入命令| B[Shell]
    B -->|翻译| C[Linux内核]
    C -->|执行结果| B
    B -->|翻译成人话| A

常见的 Shell 有:

  • bash (Bourne Again Shell) — Linux 默认主角
  • zsh (Z Shell) — 终端爱好者的小宝贝
  • fish (Friendly Interactive Shell) — 新手之友

查看当前使用的 Shell:

1
2
3
4
5
6
7
8
9
echo $SHELL
# /bin/bash  (或者 /bin/zsh,看你心情)

# 查看系统里有哪些 Shell
cat /etc/shells
# /bin/sh
# /bin/bash
# /usr/bin/zsh
# /usr/bin/fish

53.3 什么是 Shell 脚本?

简单来说,Shell 脚本就是把一堆命令写到一个文件里,让电脑自动执行

就像写剧本一样——你告诉电脑:“先做这个,再做那个,如果出现这种情况,就那样处理。”

为什么需要 Shell 脚本?

场景一:批量处理文件

1
2
3
4
5
6
7
8
# 手动操作:给100张图片加水印
# "救命,我不想一件一件做啊!"

# 脚本方案:
for img in *.jpg; do
    convert "$img" -quality 85 "compressed_$img"
done
# 一行命令,100张图片搞定!咖啡还没凉~

场景二:自动化部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 手动部署:git pull → npm install → pm2 restart
# 每次更新都要重复3步,有没有更优雅的方式?

# 脚本方案:写一个 deploy.sh
#!/bin/bash
git pull
npm install
pm2 restart all
echo "部署完成,系统正在运行~"
# 以后只需要执行 ./deploy.sh,完美!

场景三:定时任务

1
2
3
4
5
6
# "每天凌晨3点自动备份数据库"
# 手动操作:凌晨3点爬起来?不要命了?

# 脚本 + crontab = 完美解决方案
# 写好脚本,交给 crontab 去调度
# 安心睡觉,第二天上班发现备份已经乖乖躺好了

53.4 Shell 脚本能做什么?

应用场景示例
自动化运维批量部署、配置管理、定时任务
数据处理日志分析、文件整理、格式转换
系统监控检查服务状态、发送告警通知
备份恢复自动备份数据库、清理过期文件
开发辅助编译项目、代码格式化、批量测试

53.5 第一个 Shell 脚本

让我们写一个"Hello World"脚本,感受一下 Shell 脚本的魅力:

1
2
3
4
5
6
#!/bin/bash
# 我的第一个脚本:hello_world.sh

echo "Hello World!"
echo "你好,世界!"
echo "今天也是元气满满的一天呢~"

脚本解读:

  • #!/bin/bashShebang(读作 “shee-bang”),告诉系统用 /bin/bash 来执行这个脚本
    • #! 是特殊的"魔法标记",# 在 shell 中是注释,但 #! 组合在一起就是 Shebang
    • 后面的路径 /bin/bash 指定了解释器的位置
  • # 后面的内容是注释,不会被执行
  • echo 就是打印输出,类似 C 语言的 printf,Python 的 print

运行效果:

1
2
3
4
5
$ chmod +x hello_world.sh    # 先给脚本加执行权限
$ ./hello_world.sh           # 运行它!
Hello World!
你好,世界!
今天也是元气满满的一天呢~

53.6 脚本的后缀名重要吗?

技术上讲,.sh 后缀名只是给人看的,Linux 不关心这个。重要的是第一行的 #!/bin/bash

但为了可读性,建议:

  • 脚本文件用 .sh 后缀
  • 或者干脆不用后缀,比如 deploybackup
1
2
3
# 这些都能正常运行:
mv hello_world.sh hello_world     # 去掉 .sh
./hello_world                      # 照样执行

53.7 小结

概念说明
Shell命令行解释器,人和 Linux 之间的翻译官
Shell 脚本把命令写进文件,让电脑自动执行
Shebang#!/bin/bash,告诉系统用什么解释器
chmod +x给脚本加执行权限
echo输出命令,类似 print

💡 温馨提示: Shell 脚本看起来简单,但它是 Linux 世界的"瑞士军刀"!熟练掌握后,你可以用几行代码完成原本需要几个小时的工作。说不定隔壁工位的同事看了都会竖起大拇指 👍


本章小结

本章我们认识了 Shell 脚本这位"私人秘书":

  • Shell 是命令行解释器,bash 是最常用的版本
  • Shell 脚本就是"把命令写成文件,让电脑自动执行"
  • 它的优势在于自动化——批量处理、定时任务、减少重复劳动
  • 简单到 echo "Hello World",复杂到整套自动化部署系统
  • 记得 #!/bin/bashchmod +x,这是脚本的"身份证"

下一章我们将学习如何写一个真正有用的脚本,包括变量、条件判断、循环等。准备好了吗?让我们继续探索 Bash 的魔法世界! 🚀


53.2 第一个脚本

光说不练假把式,让我们写一个真正能"干活"的脚本!

创建脚本文件

1
2
3
4
5
6
7
# 创建脚本文件
vim greeting.sh

# 或者用你喜欢的编辑器
nano greeting.sh
gedit greeting.sh   # 如果在图形界面
code greeting.sh    # 如果装了 VSCode

编写脚本内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
# greeting.sh - 我的第一个正经脚本

# 定义变量
name="小明"
date=$(date +%Y年%m月%d日)

# 输出问候语
echo "========================================"
echo "  您好,$name!"
echo "  今天是:$date"
echo "========================================"
echo ""
echo "欢迎来到 Linux 的世界!"
echo "在这里,你就是主宰一切的上帝!"

运行脚本

1
2
3
4
5
6
7
8
9
# 方法一:加执行权限后运行(推荐)
chmod +x greeting.sh
./greeting.sh

# 方法二:用 bash 解释器直接运行(不需要加执行权限)
bash greeting.sh

# 方法三:用 sh 运行(POSIX 标准,不保证 bash 特性可用)
sh greeting.sh

运行效果:

========================================
  您好,小明!
  今天是:2026年03月24日
========================================

欢迎来到 Linux 的世界!
在这里,你就是主宰一切的上帝!

脚本的组成结构

一个完整的脚本通常包含以下部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash                                    # 1. Shebang(必须!)

# =========================================     # 2. 注释(说明脚本功能)
# 脚本名称:greeting.sh
# 作者:小明
# 日期:2026-03-24
# 描述:一个简单的问候脚本

# =========================================     # 3. 变量定义
NAME="Linux"
VERSION="1.0"

# =========================================     # 4. 主逻辑
echo "Hello, $NAME!"
echo "Version: $VERSION"

# =========================================     # 5. 退出
exit 0                                         # 0 表示正常退出

53.3 脚本执行

执行方式对比

方式命令需要执行权限特点
加权限执行./script.sh✅ 是最标准的方式
bash执行bash script.sh❌ 否跨 Shell 运行
source执行source script.sh❌ 否在当前Shell运行

source vs bash 的区别

这个区别超级重要,很多新手都会在这里翻车!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 创建一个脚本 test_env.sh
#!/bin/bash
export MY_VAR="Hello from script"

# 用 bash 运行
bash test_env.sh
echo $MY_VAR      # 输出为空!变量不存在!

# 用 source 运行
source test_env.sh
echo $MY_VAR      # 输出:Hello from script

解释:

  • bash script.sh — 在子 Shell 中运行,环境隔离
  • source script.sh — 在当前 Shell 中运行,环境共享

什么时候用哪种?

1
2
3
4
5
6
7
8
9
# 场景一:运行独立脚本
./my_script.sh              # 用 ./ ,子 shell 运行

# 场景二:加载环境变量
source /etc/profile         # 让环境变量生效
source ~/.bashrc            # 重新加载 bash 配置

# 场景三:调试脚本
bash -x script.sh           # 调试模式运行

管道执行

1
2
3
4
5
6
7
8
# 把脚本输出通过管道传给其他命令
./script.sh | grep "error"

# Here Document 方式
./script.sh << EOF
input1
input2
EOF

53.4 变量

终于到了变量部分!这是脚本的"记忆单元",让你的脚本不再"金鱼脑"。

变量定义

1
2
3
4
5
6
7
8
9
# 基本赋值(等号两边不能有空格!)
name="Linux"           # 字符串
version=1.0            # 数字(其实还是字符串)
is_awesome=true       # 布尔值(习惯用法)
empty_var=             # 空值

# 定义常量(习惯上用大写)
readonly PI=3.14159
MAX_RETRIES=3

变量使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 使用变量时要加 $
echo $name
echo "Hello, $name"
echo "Hello, ${name}"    # 加花括号更规范

# 字符串拼接
greeting="Hello, "$name"!"
echo $greeting           # Hello, Linux!

# 双引号里可以解析变量,单引号不会
echo "$name is best"     # Linux is best
echo '$name is best'     # $name is best(字面量)

系统变量

变量说明示例
$0脚本名greeting.sh
$1-$9第1-9个参数$1 第一个参数
$#参数个数3
$@所有参数"$1" "$2" "$3"
$$当前进程ID12345
$?上个命令退出状态0 表示成功
$USER当前用户名linux
$HOME家目录/home/linux
$PWD当前目录/home/linux/scripts
$RANDOM随机数4823

命令替换

把命令输出赋值给变量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 方法一:反引号(老派)
current_date=`date +%Y-%m-%d`

# 方法二:$()(现代派,推荐!)
current_time=$(date +%H:%M:%S)
disk_usage=$(df -h / | tail -1)
file_count=$(ls | wc -l)

# 嵌套使用
echo "Today is $(date +%Y年%m月%d日)"

数组变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 定义数组
colors=("红色" "绿色" "蓝色" "黄色")

# 访问数组元素
echo ${colors[0]}    # 红色
echo ${colors[1]}    # 绿色

# 获取所有元素
echo ${colors[@]}    # 红色 绿色 蓝色 黄色

# 获取数组长度
echo ${#colors[@]}  # 4

# 获取某个元素的长度
echo ${#colors[0]}   # 2("红色"两个字符)

只读变量

1
2
readonly CONST_VAR="这个变量不能改"
CONST_VAR="试试改一下"   # 报错!

删除变量

1
2
3
4
name="Linux"
echo $name      # Linux
unset name
echo $name      # 空,什么都没有了

53.5 引号

引号在 Shell 中可不是随便用的,它们有各自的"性格"。

单引号 vs 双引号

类型特点示例
单引号 ' '原样输出,不解析任何特殊字符'$name'$name
双引号 " "解析变量和某些转义字符"$name"Linux
反引号 ` `执行命令(已过时)`date`

实战演示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
name="Linux"

# 单引号:所见即所得
echo '$name'           # 输出:$name
echo 'Hello\nWorld'    # 输出:Hello\nWorld(不转义)

# 双引号:解析变量和特殊字符
echo "$name"           # 输出:Linux
echo "Hello\nWorld"    # 输出:Hello(换行)

# 变量和字符串拼接
echo "Hello, $name!"   # 输出:Hello, Linux!
echo "Version: ${version:-1.0}"  # 默认值语法

# 转义字符
echo "Path: \$HOME"    # 输出:Path: $HOME
echo "He said \"Hi\""  # 输出:He said "Hi"

Here Document

用于输入多行文本给命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 基本语法
command << EOF
第一行内容
第二行内容
$variable(可解析变量)
EOF

# 实例:多行字符串
cat << EOF
=================================
    用户信息报表
=================================
用户名:$USER
日期:$(date +%Y-%m-%d)
主机:$HOSTNAME
=================================
EOF

Here String

1
2
3
4
5
# 把字符串作为输入
cat <<< "Hello World"

# 等价于
echo "Hello World" | cat

53.6 运算符

Shell 脚本虽然不是计算器,但基本的数学运算还是得会的!

算术运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 方法一:$[表达式](已过时)
result=$[1 + 2]
echo $result    # 3

# 方法二:$((表达式))(推荐!)
result=$((1 + 2))
echo $result    # 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 += 5))   # 15
echo $((a -= 3))   # 12
echo $((a *= 2))   # 24

关系运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
x=10
y=20

# 比较(返回 true 或 false)
[ $x -eq $y ]   # 等于
[ $x -ne $y ]   # 不等于
[ $x -gt $y ]   # 大于
[ $x -lt $y ]   # 小于
[ $x -ge $y ]   # 大于等于
[ $x -le $y ]   # 小于等于

# 字符串比较
[ "$str1" = "$str2" ]   # 等于
[ "$str1" != "$str2" ]  # 不等于
[ -z "$str" ]           # 字符串为空
[ -n "$str" ]           # 字符串非空

逻辑运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 逻辑与
[ $x -gt 5 ] && [ $x -lt 20 ]

# 逻辑或
[ $x -lt 5 ] || [ $x -gt 20 ]

# 逻辑非
[ ! $x -eq 10 ]

# 组合条件
if [ $x -gt 5 ] && [ $x -lt 20 ]; then
    echo "x 在 5 和 20 之间"
fi

文件测试运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
file="/path/to/file"

# 文件类型测试
[ -e $file ]   # 文件存在
[ -f $file ]   # 普通文件
[ -d $file ]   # 目录
[ -L $file ]   # 符号链接
[ -r $file ]   # 可读
[ -w $file ]   # 可写
[ -x $file ]   # 可执行

# 文件属性比较
[ file1 -nt file2 ]   # file1 比 file2 新
[ file1 -ot file2 ]   # file1 比 file2 旧

53.7 条件判断

程序员的三大法宝:顺序、选择、循环。现在我们学"选择"!

基本语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 单分支
if [ 条件 ]; then
    # 条件成立时执行
fi

# 双分支
if [ 条件 ]; then
    # 条件成立
else
    # 条件不成立
fi

# 多分支
if [ 条件1 ]; then
    # 条件1成立
elif [ 条件2 ]; then
    # 条件2成立
else
    # 都不成立
fi

实战例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash
# check_number.sh - 判断数字大小

number=15

if [ $number -gt 20 ]; then
    echo "$number 比 20 大"
elif [ $number -gt 10 ]; then
    echo "$number 比 10 大,比 20 小"
else
    echo "$number 不超过 10"
fi

# 输出:15 比 10 大,比 20 小

53.8 if 语句

if 语句是条件判断的核心,让我们深入了解一下。

嵌套 if

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
age=25
income=50000

if [ $age -ge 18 ]; then
    echo "已成年"
    
    if [ $income -gt 100000 ]; then
        echo "高收入人群"
    else
        echo "普通收入"
    fi
    
else
    echo "未成年"
fi

多个条件

1
2
3
4
5
6
7
8
9
# 并且 &&
if [ $age -ge 18 ] && [ $age -lt 65 ]; then
    echo "劳动力人口"
fi

# 或者 ||
if [ $status == "admin" ] || [ $status == "root" ]; then
    echo "管理员权限"
fi

test 命令

[ ] 其实是 test 命令的简写:

1
2
3
4
5
6
# 这两个等价:
test $x -eq $y
[ $x -eq $y ]

# test 命令可以直接在命令行使用
test -f /etc/passwd && echo "文件存在"

53.9 case 语句

当有很多分支时,caseif...elif...elif... 优雅得多!

基本语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
case 变量 in
模式1)
    # 匹配模式1时执行
    ;;
模式2)
    # 匹配模式2时执行
    ;;
*)
    # 默认情况(类似 else)
    ;;
esac

实战例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# menu.sh - 简单的菜单选择

echo "请选择你喜欢的操作系统:"
echo "1) Linux"
echo "2) macOS"
echo "3) Windows"
echo ""

read choice

case $choice in
    1)
        echo "明智的选择!Linux 最棒!"
        ;;
    2)
        echo "macOS 也很不错,设计优美"
        ;;
    3)
        echo "Windows...呃,好吧"
        ;;
    *)
        echo "无效选择,请输入 1-3"
        ;;
esac

模式匹配

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 数字范围
case $score in
    9[0-9]|100)
        echo "优秀"
        ;;
    [7-8][0-9])
        echo "良好"
        ;;
    6[0-9])
        echo "及格"
        ;;
    *)
        echo "需要努力"
        ;;
esac

# 字符串模式
case $filename in
    *.jpg|*.png)
        echo "图片文件"
        ;;
    *.txt)
        echo "文本文件"
        ;;
    *.sh)
        echo "Shell 脚本"
        ;;
    *)
        echo "未知类型"
        ;;
esac

53.10 for 循环

for 循环是批量处理的利器!

基本语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 形式一:遍历列表
for 变量 in 值1 值2 值3
do
    # 循环体
done

# 形式二:C 语言风格
for (( i=0; i<5; i++ ))
do
    # 循环体
done

实战例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/bin/bash
# 遍历文件
for file in *.txt
do
    echo "处理文件: $file"
done

# 遍历数字
for i in {1..5}
do
    echo "第 $i 次迭代"
done

# C 语言风格
for (( i=1; i<=5; i++ ))
do
    echo "计数: $i"
done

# 遍历命令输出
for line in $(cat users.txt)
do
    echo "用户: $line"
done

# 遍历数组
colors=("红" "绿" "蓝")
for color in "${colors[@]}"
do
    echo "颜色: $color"
done

批量重命名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 给所有 .txt 文件加上 .bak 后缀
for file in *.txt; do
    mv "$file" "${file}.bak"
done

# 批量转换文件名
for img in *.JPG; do
    newname=$(basename "$img" .JPG).jpg
    mv "$img" "$newname"
done

53.11 while 循环

while 循环当条件满足时一直执行。

基本语法

1
2
3
4
while [ 条件 ]
do
    # 循环体
done

实战例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
# 计数器示例
count=1

while [ $count -le 5 ]
do
    echo "计数: $count"
    count=$((count + 1))
done

# 读取文件行
while read line
do
    echo "行内容: $line"
done < /etc/hostname

# 无限循环(Ctrl+C 停止)
while true
do
    echo "按 Ctrl+C 停止..."
    sleep 1
done

until 循环

until 和 while 相反,条件不满足时继续执行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
# until 示例:倒计时

count=5

until [ $count -eq 0 ]
do
    echo "倒计时: $count"
    sleep 1
    count=$((count - 1))
done

echo "发射!🚀"

53.12 until 循环

until 循环是 while 的"反义词"——当条件为 false 时继续执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
# until 示例:等待服务启动

port=3306

until nc -z localhost $port 2>/dev/null
do
    echo "MySQL 还未启动,等待中..."
    sleep 2
done

echo "MySQL 已就绪!"

53.13 函数

函数是代码复用的神器!

定义函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 形式一:function 关键字
function greet {
    echo "你好!"
}

# 形式二:直接定义(更简洁)
greet() {
    echo "你好!"
}

# 调用函数
greet

函数参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash

# 带参数的函数
greet_user() {
    name=$1           # 第一个参数
    age=$2            # 第二个参数
    echo "你好,$name!你 $age 岁了。"
    
    # 返回值
    return 0
}

# 调用并传递参数
greet_user "小明" "25"

# 使用 $? 获取返回值
echo "函数返回状态: $?"

函数返回值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# return 用于返回状态码(0-255)
check_file() {
    if [ -f "$1" ]; then
        return 0    # 成功
    else
        return 1    # 失败
    fi
}

if check_file "/etc/passwd"; then
    echo "文件存在"
else
    echo "文件不存在"
fi

# 如果要返回数据,用 echo
get_date() {
    echo $(date +%Y-%m-%d)
}

today=$(get_date)
echo "今天是: $today"

局部变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/bash

counter() {
    local count=0    # local 关键字定义局部变量
    count=$((count + 1))
    echo "函数内 count: $count"
}

count=100
counter
echo "函数外 count: $count"   # 仍然是 100,函数内的改动不影响外部

函数库

把常用函数写到一个文件里,需要时 source 加载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 创建文件 common.sh
#!/bin/bash

# 颜色输出函数
red() { echo -e "\033[31m$*\033[0m"; }
green() { echo -e "\033[32m$*\033[0m"; }
yellow() { echo -e "\033[33m$*\033[0m"; }

# 日志函数
log() {
    echo "[$(date +%H:%M:%S)] $*"
}

# 在其他脚本中使用
#!/bin/bash
source common.sh

green "操作成功!"
log "开始处理..."

53.14 数组

数组让你能存储和操作一组数据。

定义数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 方式一:直接赋值
fruits=("苹果" "香蕉" "橙子" "葡萄")

# 方式二:按索引赋值
colors[0]="红色"
colors[1]="绿色"
colors[2]="蓝色"

# 方式三:稀疏数组
sparse[0]="第一"
sparse[5]="第六"    # 中间跳过了

# 方式四:从命令输出创建
files=($(ls *.txt))

访问数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
fruits=("苹果" "香蕉" "橙子" "葡萄")

# 访问单个元素
echo ${fruits[0]}    # 苹果
echo ${fruits[2]}    # 橙子

# 访问所有元素
echo ${fruits[@]}    # 苹果 香蕉 橙子 葡萄
echo ${fruits[*]}    # 苹果 香蕉 橙子 葡萄

# 获取数组长度
echo ${#fruits[@]}   # 4
echo ${#fruits[*]}   # 4

# 获取某个元素的长度
echo ${#fruits[0]}   # 2("苹果"两个字符)

数组切片

1
2
3
4
5
6
7
fruits=("苹果" "香蕉" "橙子" "葡萄" "西瓜")

# 切片:从索引 1 开始,取 3 个
echo ${fruits[@]:1:3}   # 香蕉 橙子 西瓜

# 从索引 2 开始到末尾
echo ${fruits[@]:2}     # 橙子 葡萄 西瓜

数组操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 添加元素
fruits=("苹果" "香蕉")
fruits+=("橙子")         # 追加
fruits=("${fruits[@]}" "葡萄")  # 另一种方式

# 删除元素
unset fruits[1]          # 删除索引 1 的元素

# 删除整个数组
unset fruits

# 关联数组(需要 bash 4+)
declare -A person
person["name"]="小明"
person["age"]=25
person["city]="北京"

echo ${person["name"]}   # 小明
echo ${!person[@]}        # name age city(所有键)

遍历数组

1
2
3
4
5
6
7
8
9
# 遍历值
for fruit in "${fruits[@]}"; do
    echo "水果: $fruit"
done

# 遍历索引
for i in "${!fruits[@]}"; do
    echo "索引 $i: ${fruits[$i]}"
done

53.15 脚本调试

调试是发现和修复问题的过程,Shell 提供了多种调试工具。

调试选项

选项作用
-n检查语法错误,不执行
-v显示读取的每一行
-x显示执行的每一行(追踪)

使用方法

1
2
3
4
5
6
7
8
# 检查语法
bash -n script.sh

# 显示执行过程
bash -x script.sh

# 组合使用
bash -vn script.sh

在脚本中启用调试

1
2
3
4
5
6
7
8
#!/bin/bash

# 在需要调试的位置开启
set -x    # 开启调试
echo "这行会被追踪"
set +x    # 关闭调试

echo "这行不会"

常见调试技巧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# debug_demo.sh

# 1. 打印变量值
echo "DEBUG: x = $x"

# 2. 在可疑位置加日志
log_debug() {
    echo "[$(date +%T)] DEBUG: $*"
}

# 3. 捕获错误
set -e    # 遇错误就退出
set -u    # 使用未定义变量时报错
set -o pipefail   # 管道中任何一个失败就算失败

# 4. 调试函数
debug_func() {
    echo "Entering: ${FUNCNAME[1]}"
    echo "Arguments: $*"
}

trap 信号捕获

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash
# trap_demo.sh

# 脚本退出时执行清理
cleanup() {
    echo "脚本退出,清理临时文件..."
    rm -f /tmp/tmp_*.txt
}

trap cleanup EXIT

# 脚本主体
echo "开始执行..."
# ...

常用调试命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看命令的 help
help set

# 列出所有函数及其定义
declare -F

# 查看函数定义
declare -f function_name

# 跟踪变量赋值
bash -x script.sh 2>&1 | grep variable_name

本章小结

本章我们学习了 Bash 脚本的基础知识:

知识点内容
Shell 脚本把命令写成文件,让电脑自动执行
Shebang#!/bin/bash,告诉系统用什么解释器
变量存储数据的"容器",用 $ 访问
引号单引号原样输出,双引号解析变量
运算符算术、关系、逻辑、文件测试
条件判断if...then...fi
多分支case...in...esac
循环forwhileuntil
函数代码复用,function name()name()
数组存储多个值,${array[@]} 访问
调试bash -xset -xtrap

Shell 脚本是 Linux 运维的基石。掌握好这些基础知识,你就能写出强大的自动化脚本,让电脑乖乖为你打工!


💡 温馨提示: 写 Shell 脚本最重要的是多练!建议把本章的每个例子都亲手敲一遍,感受一下"让电脑听话"的乐趣。下一个脚本大师,就是你!


下一章预告:53.2-53.15 节已更新完毕!第五十四章我们将学习 Bash 脚本进阶,包括字符串处理、正则表达式、sed、awk 等强大工具。敬请期待! 🚀

最后修改 March 24, 2026: 新增JavaScript教程 (37305c4)