跳到主要内容

Shell 脚本编程教程

欢迎学习 Shell 脚本编程!本教程将带你从零基础开始,系统掌握 Shell 脚本开发的核心技能。

什么是 Shell?

Shell 是用户与操作系统内核之间的桥梁,它是一个命令行解释器,负责接收用户输入的命令并将其传递给内核执行。Shell 既是命令解释器,也是一种强大的编程语言。

Shell 的双重身份

作为命令解释器,Shell 提供了与系统交互的界面。你可以输入 ls 查看目录,输入 cd 切换目录,输入 grep 搜索文本。这些交互式操作是 Shell 最直观的使用方式。

作为编程语言,Shell 提供了变量、函数、流程控制、循环等完整的编程特性。你可以将一系列命令组合成脚本文件,实现任务自动化。比如每天自动备份数据库、批量处理日志文件、自动化部署应用等。

常见的 Shell 类型

Unix 和 Linux 系统上有多种 Shell 实现,它们各有特点:

Bash(Bourne Again Shell) 是最广泛使用的 Shell。它是 GNU 项目的核心组件,几乎所有的 Linux 发行版都将其作为默认 Shell。Bash 继承了 Bourne Shell 的语法,同时融合了 Korn Shell 和 C Shell 的优秀特性。本教程以 Bash 为主进行讲解。

Zsh(Z Shell) 是功能更强大的现代 Shell。它提供了更好的命令补全、拼写纠错、主题定制等功能。macOS Catalina 之后已将默认 Shell 从 Bash 改为 Zsh。许多开发者使用 Oh My Zsh 框架来增强 Zsh 的使用体验。

Fish(Friendly Interactive Shell) 专注于用户友好性。它开箱即用,提供了智能的自动建议、语法高亮、简洁的脚本语法。对于初学者来说,Fish 是一个很好的选择。

Dash(Debian Almquist Shell) 是一个轻量级的 POSIX 兼容 Shell。它比 Bash 更快、占用更少内存,常用于系统脚本执行。在 Ubuntu 等系统中,/bin/sh 通常链接到 Dash。

你可以通过以下命令查看当前系统可用的 Shell:

cat /etc/shells

查看当前正在使用的 Shell:

echo $SHELL

查看 Bash 版本:

bash --version

为什么学习 Shell 脚本?

Shell 脚本是系统管理员和开发者的必备技能,原因如下:

自动化运维:服务器管理、日志分析、备份恢复、监控告警等日常运维工作都可以通过 Shell 脚本自动化。一个精心编写的脚本可以替代大量重复的手动操作。

开发效率:编译构建、代码生成、测试执行、环境部署等开发流程可以通过脚本串联起来。持续集成和持续部署(CI/CD)的很多环节都依赖 Shell 脚本。

系统深入理解:学习 Shell 脚本会让你更深入地理解 Linux 系统的工作原理。文件系统、进程管理、权限控制、网络配置等概念都会在脚本编写中得到实践。

跨平台兼容:Shell 脚本可以在几乎所有 Unix-like 系统上运行,包括 Linux、macOS、BSD 等。通过 WSL(Windows Subsystem for Linux)或 Git Bash,Windows 用户也可以运行 Shell 脚本。

快速原型开发:对于一些简单的任务,使用 Shell 脚本比编写 C、Python 等程序更快。你可以直接调用系统命令和现有工具,无需编译即可运行。

Shell 脚本的基本结构

一个简单的 Shell 脚本通常包含以下部分:

#!/bin/bash

# 这是注释,用于说明脚本的功能
# 作者:Your Name
# 日期:2024-01-01

# 定义变量
NAME="World"

# 输出信息
echo "Hello, $NAME!"

# 执行命令
date

第一行 #!/bin/bash 称为 shebang(也叫 hashbang),它告诉系统使用哪个解释器来执行这个脚本。#! 是固定格式,后面跟着解释器的路径。对于 Bash 脚本,通常是 #!/bin/bash#!/usr/bin/env bash

使用 #!/usr/bin/env bash 的好处是它会在系统的 PATH 环境变量中查找 bash,这样脚本更具可移植性。但某些安全敏感的环境可能限制使用 env,这时就需要使用绝对路径。

注释以 # 开头,从 # 到行末的内容都会被忽略。良好的注释习惯是脚本可维护性的重要保障。

创建和执行脚本

创建脚本文件

使用文本编辑器创建一个以 .sh 为扩展名的文件:

vim hello.sh

输入以下内容:

#!/bin/bash
echo "Hello, Shell!"

赋予执行权限

在 Linux 系统中,文件默认没有执行权限。你需要使用 chmod 命令添加执行权限:

chmod +x hello.sh

执行脚本

有三种方式可以执行脚本:

方式一:作为可执行程序执行

./hello.sh

这种方式需要脚本有执行权限,且需要指定路径(即使是当前目录也要加 ./)。

方式二:指定解释器执行

bash hello.sh

这种方式不需要脚本有执行权限,因为你是直接调用 bash 解释器来执行脚本文件。

方式三:使用 source 或点命令

source hello.sh
# 或者
. hello.sh

这种方式会在当前 Shell 环境中执行脚本,而不是创建一个新的子 Shell。这意味着脚本中对变量的修改会影响当前 Shell。这种方式常用于加载配置文件或环境变量。

三种执行方式的区别

理解这三种执行方式的区别非常重要:

当使用 ./hello.shbash hello.sh 执行时,脚本在一个新的子 Shell 进程中运行。脚本中定义的变量、函数在脚本执行完毕后就消失了,不会影响父 Shell。

当使用 source hello.sh 执行时,脚本在当前 Shell 中运行。脚本中定义的变量、函数会保留在当前 Shell 中。这就是为什么我们使用 source ~/.bashrc 来重新加载配置文件。

你可以通过以下示例来验证:

#!/bin/bash
# 文件名:test_var.sh
MY_VAR="Hello from script"
echo "In script: $MY_VAR"

执行并观察:

# 使用 bash 执行
bash test_var.sh
# 输出:In script: Hello from script

# 尝试在当前 Shell 访问变量
echo $MY_VAR
# 输出为空,因为变量不存在

# 使用 source 执行
source test_var.sh
# 输出:In script: Hello from script

# 再次访问变量
echo $MY_VAR
# 输出:Hello from script

Shell 脚本的执行流程

当 Shell 执行一个脚本时,它会经历以下步骤:

  1. 读取脚本文件:Shell 读取脚本文件的内容到内存。

  2. 词法分析:将代码分解为一个个 token(标记),包括关键字、变量、操作符、字符串等。

  3. 语法分析:根据 Shell 的语法规则,将 token 组织成语法树。

  4. 执行命令:按照语法树的结构,依次执行各个命令。

  5. 返回退出状态:脚本执行完毕后,返回一个退出状态码。0 表示成功,非 0 表示失败。

退出状态码

每个命令执行完毕后都会返回一个退出状态码(exit status),这是一个 0 到 255 之间的整数。

  • 0:表示命令执行成功
  • 非 0:表示命令执行失败,不同的值代表不同的错误类型

你可以通过 $? 变量获取上一个命令的退出状态码:

ls /etc/passwd
echo $? # 输出 0,表示成功

ls /nonexistent
echo $? # 输出非 0,表示失败

在脚本中,你可以使用 exit 命令指定退出状态码:

#!/bin/bash

if [ $# -eq 0 ]; then
echo "Usage: $0 <filename>"
exit 1 # 参数错误,返回 1
fi

if [ ! -f "$1" ]; then
echo "Error: File not found"
exit 2 # 文件不存在,返回 2
fi

echo "Processing $1..."
exit 0 # 成功,返回 0

Shell 脚本的编码规范

良好的编码规范可以提高脚本的可读性和可维护性:

使用有意义的变量名:变量名应该能够表达其用途。file_countfc 更好,max_connectionsmc 更清晰。

添加适当的注释:解释脚本的用途、参数的含义、复杂逻辑的思路。但不要过度注释,代码本身应该尽可能清晰。

使用缩进:统一使用 2 个空格或 4 个空格进行缩进,保持风格一致。

错误处理:检查命令的执行结果,处理可能出现的错误情况。

使用 set 选项:在脚本开头添加 set -e 可以让脚本在任何命令失败时立即退出,避免错误累积。

#!/bin/bash
set -e # 任何命令失败时立即退出
set -u # 使用未定义变量时报错
set -o pipefail # 管道中任何命令失败时返回失败状态

教程目录

基础部分

进阶部分

高级部分

参考资料

  • 速查表 - Shell 常用语法和命令速查

学习建议

动手实践:Shell 脚本是实践性很强的技能。每学一个知识点,都要亲自敲代码验证。不要只是阅读,要运行、修改、实验。

从简单开始:先编写简单的脚本,比如批量重命名文件、定时备份目录。随着经验积累,再尝试更复杂的任务。

善用帮助文档:使用 man bash 查看 Bash 手册,使用 help command 查看内置命令的帮助,使用 command --help 查看外部命令的帮助。

阅读优秀脚本:GitHub 上有很多优秀的 Shell 脚本项目,阅读它们的源码可以学到很多技巧和最佳实践。

注意可移植性:如果你的脚本需要在多个系统上运行,要注意不同 Shell 和不同系统之间的差异。使用 POSIX 兼容的语法可以提高可移植性。

参考资料

准备好开始学习了吗?让我们从基础语法开始,逐步深入 Shell 脚本编程的世界!