Linux系统中的Shell是一个特殊的应用程序,它介于操作系统内核与用户之间,充当了一个“命令解释器”的角色,负责接收用户输入的操作指令并进行解释,将需要执行的操作传递给内核执行,并输出执行结果
各种Shell环境中都使用到了“变量”的概念。Shell变量用来存放系统和用户需要使用的特定参数,而且这些参数可以根据用户的设定或系统环境的变化而相应变化。通过使用变量,Shell程序能够提供更加灵活的功能,适应性更强。
常见Shell变量的类型包括自定义变量,环境变量,只读变量,位置变量,预定义变量
自定义变量是由系统用户自己定义的变量,只有用户自己的Shell环境中有效,因此又称为本地变量。在编写Shell脚本程序时,通常会设置一些特定的自定义变量,以适应程序执行过程中的各种变化,满足不同的需要
Shell 编程基础规范
规范的编写风格能大幅提升脚本的可读性、可维护性和兼容性,以下是行业通用的核心规范:
1. 脚本头部声明(Shebang)
- 必须在脚本第一行指定解释器,推荐使用 bash(而非默认 sh)以兼容更多特性:
bash
运行
#!/bin/bash
- 若需脚本在不同环境兼容,可使用更灵活的写法(优先找系统中的 bash):
bash
运行
#!/usr/bin/env bash
- 禁止省略 Shebang,否则系统会使用默认 shell(可能导致语法兼容问题)。
2. 脚本编码与注释
- 编码:统一使用 UTF-8 编码,避免中文乱码(可在脚本开头加 export LANG=en_US.UTF-8)。
- 注释规范:
- 脚本头部:必须包含脚本说明、作者、日期、功能、参数说明(示例):
bash
运行
#!/bin/bash
# 脚本名称:backup_data.sh
# 作 者:Your Name
# 日 期:2026-02-24
# 功 能:备份指定目录的文件到远程服务器
# 参 数:$1 – 待备份目录;$2 – 备份保留天数
# 版 本:v1.0 - 单行注释:用 # 开头,注释内容与 # 之间保留一个空格(如 # 检查目录是否存在)。
- 复杂逻辑:关键代码块前加多行注释说明核心思路,避免 “过度注释”(无需注释显而易见的代码)。
- 脚本头部:必须包含脚本说明、作者、日期、功能、参数说明(示例):
3. 命名规范
表格
| 脚本文件 | 小写字母 + 下划线,.sh 后缀 | system_monitor.sh |
| 变量名 | 大写字母 + 下划线 | BACKUP_DIR、LOG_FILE |
| 函数名 | 小写字母 + 下划线 | check_disk_usage |
| 临时文件 / 目录 | 包含脚本名 + 随机串 | tmp_backup_$$.log |
4. 格式与缩进
- 缩进:统一使用 4 个空格(禁止用 Tab,避免不同编辑器显示不一致)。
- 换行:
- 管道符 |、重定向 >、逻辑符 &&/|| 等换行时放在行尾,增强可读性:
bash
运行
# 推荐
ls -l /var/log \\
| grep "error" \\
| awk '{print $9}' > error_logs.txt# 不推荐(一行过长)
ls -l /var/log | grep "error" | awk '{print $9}' > error_logs.txt
- 管道符 |、重定向 >、逻辑符 &&/|| 等换行时放在行尾,增强可读性:
- 语句块:if/for/while 等语句的 then/do 换行写,fi/done 与开头对齐:
bash
运行
# 推荐
if [ -d "$BACKUP_DIR" ]; then
echo "备份目录已存在"
else
mkdir -p "$BACKUP_DIR"
fi# 不推荐(紧凑写法,可读性差)
if [ -d "$BACKUP_DIR" ]; then echo "存在"; fi
5. 安全与兼容性规范
- 开启严格模式:脚本开头添加以下内容,强制检查错误,避免静默失败:
bash
运行
# 遇到错误立即退出
set -e
# 未定义变量使用时立即退出
set -u
# 管道中任意命令失败则整个管道失败
set -o pipefail
# 显示执行的每一条命令(调试用,正式环境可注释)
# set -x - 路径与命令:
- 使用绝对路径调用命令(如 /usr/bin/ls 而非 ls),避免环境变量 PATH 篡改导致执行错误命令;
- 路径拼接时用 cd "$(dirname "$0")" 获取脚本所在目录,避免脚本在不同路径执行时路径错误。
- 特殊字符处理:所有变量引用必须加双引号 "$VAR",避免空格、通配符(*/?)导致解析错误:
bash
运行
# 错误示例(目录含空格时会报错)
DIR="my backup"
ls $DIR # 等价于 ls my backup(解析为两个参数)# 正确示例
ls "$DIR" # 等价于 ls "my backup"
二、Shell 变量详解
Shell 变量是存储数据的核心载体,分为自定义变量、环境变量、位置变量、特殊变量四类,以下是详细用法:
1. 变量的基本操作
(1)定义变量
- 语法:变量名=值(等号两侧无空格,空格会被解析为命令分隔):
bash
运行
# 正确
NAME="张三"
AGE=25
PI=3.14# 错误(等号两侧有空格)
NAME = "张三" # 系统会把 NAME 当作命令执行 - 特殊值处理:值包含空格 / 特殊字符时,必须用单引号 / 双引号包裹:
bash
运行
# 双引号:支持变量解析、转义符
MSG="Hello, $NAME! \\n当前目录:$(pwd)"# 单引号:纯字符串,不解析任何变量/转义符
MSG='Hello, $NAME! \\n当前目录:$(pwd)' # 输出原字符串
(2)读取变量
- 语法:$变量名 或 ${变量名}(推荐用 ${},避免变量名与后续字符混淆):
bash
运行
echo $NAME # 基础用法
echo "${NAME}的年龄是${AGE}" # 推荐(明确变量边界)
echo "$NAME的年龄是$AGE" # 可能出错(若后续有字符,如 $NAME1 会被解析为变量 NAME1)
(3)删除变量
- 语法:unset 变量名,删除后变量不可用(开启 set -u 时访问会报错):
bash
运行
unset AGE
echo "$AGE" # 开启 set -u 时会报错:unbound variable
(4)变量作用域
- 局部变量:默认仅在当前 Shell 进程生效,子进程 / 其他脚本不可访问;
- 全局变量(环境变量):用 export 声明,可传递给子进程:
bash
运行
export PATH="$PATH:/usr/local/bin" # 扩展系统命令路径
export LANG="zh_CN.UTF-8" # 设置字符编码
2. 常用变量分类
(1)位置变量(脚本参数)
用于接收脚本执行时传入的参数,核心如下:
表格
| $0 | 脚本本身的名称 | $0 = ./test.sh |
| $1-$9 | 第 1-9 个参数,超过 9 个用 ${10} | $1=a,$2=b,${10}=(无则为空) |
| $* | 所有参数(视为一个整体) | "$*" = "a b c" |
| $@ | 所有参数(视为独立个体,推荐使用) | "$@" = "a" "b" "c" |
| $# | 参数的个数 | $# = 3 |
示例(参数处理):
bash
运行
#!/bin/bash
set -euo pipefail
# 检查参数个数
if [ $# -lt 2 ]; then
echo "用法:$0 <源目录> <目标目录>"
exit 1 # 非0退出表示执行失败
fi
SRC_DIR="$1"
DEST_DIR="$2"
echo "源目录:$SRC_DIR"
echo "目标目录:$DEST_DIR"
echo "总参数个数:$#"
(2)特殊变量(系统内置)
Shell 内置的特殊变量,用于获取脚本执行状态、进程信息等:
表格
| $$ | 当前脚本的进程 ID(PID) | echo "脚本 PID:$$" |
| $? | 上一条命令的退出状态(0 = 成功,非 0 = 失败) | ls /tmp; echo "退出码:$?" |
| $! | 后台运行的最后一个进程的 PID | sleep 10 & echo "后台 PID:$!" |
| $_ | 上一条命令的最后一个参数 | ls /var/log; echo "$_" = /var/log |
(3)环境变量(系统预设)
系统默认定义的环境变量,可直接使用或修改:
表格
| HOME | 当前用户的家目录 | cd $HOME(回到家目录) |
| PATH | 命令搜索路径 | export PATH="$PATH:/my/bin" |
| PWD | 当前工作目录 | echo "当前目录:$PWD" |
| USER | 当前登录用户 | echo "当前用户:$USER" |
| LOGNAME | 当前用户的登录名(同 USER) | – |
| SHELL | 当前使用的 Shell 解释器 | echo "当前 Shell:$SHELL" |
| HOSTNAME | 主机名 | echo "主机名:$HOSTNAME" |
3. 变量的高级操作(字符串处理)
Shell 支持对变量(字符串)进行截取、替换、长度计算等操作,无需依赖外部命令(如 awk/sed):
表格
| 获取长度 | ${#VAR} | ${#VAR} | 15 |
| 截取开头 n 个字符 | ${VAR:0:n} | ${VAR:0:5} | hello |
| 从第 n 位截取 m 个字符 | ${VAR:n:m} | ${VAR:6:5} | world |
| 截取到最后 | ${VAR:n} | ${VAR:6} | world.txt |
| 替换第一个匹配项 | ${VAR / 旧 / 新} | ${VAR/.txt/.log} | hello_world.log |
| 替换所有匹配项 | ${VAR// 旧 / 新} | ${VAR/o/O} | hellO_wOrld.txt |
| 删除开头匹配的最短 | ${VAR# 匹配串} | ${VAR#*_} | world.txt |
| 删除开头匹配的最长 | ${VAR## 匹配串} | ${VAR##*_} | txt |
| 删除结尾匹配的最短 | ${VAR% 匹配串} | ${VAR%.*} | hello_world |
| 删除结尾匹配的最长 | ${VAR%% 匹配串} | ${ |



