在 Shell(特别是 Bash)中,特殊变量是由系统自动设置和维护的,用户通常只能读取而不能直接赋值。它们主要分为位置参数 、进程/状态变量 和Shell 选项变量三大类。
1. 位置参数(脚本传参核心)
这些变量用于获取传递给脚本或函数的参数:
| 变量 | 含义 | 备注 |
|---|---|---|
$0 |
当前脚本/命令的名称 | 包含调用时使用的路径 |
$1 ~ $9 |
第1到第9个位置参数 | 超过9个需用 ${10} 形式 |
${n} |
第 n 个位置参数 | n > 9 时必须使用花括号 |
$# |
位置参数的个数 | 不包含 $0 |
$* |
所有位置参数组成的单个字符串 | 受 IFS 影响,默认以空格分隔 |
$@ |
所有位置参数组成的独立字符串列表 | 在双引号中仍保持独立,遍历首选 |
shift [n] |
将位置参数左移 n 位 | 不是变量,但常配合位置参数使用 |
$*与$@的关键区别当被双引号包裹时:
"$*"→ 将所有参数合并为一个 字符串"arg1 arg2 arg3""$@"→ 每个参数仍是独立的 字符串"arg1" "arg2" "arg3"在
for循环遍历参数时,始终使用"$@"以避免含空格的参数被错误拆分。
2. 进程与状态变量
| 变量 | 含义 | 典型用途 |
|---|---|---|
$? |
上一条命令的退出状态码 | 0 表示成功,非零表示失败 |
$$ |
当前 Shell 进程的 PID | 生成唯一临时文件名 |
$! |
最近一个后台任务的 PID | 记录并后续 wait 或 kill |
$- |
当前 Shell 的选项标志集合 | 如 himBHs 表示开启了历史展开等 |
$_ |
上一条命令的最后一个参数 | 交互式快速引用上条命令的路径 |
3. 常用内置环境变量
虽然严格意义上属于环境变量,但在 Shell 编程中作为特殊变量高频使用:
| 变量 | 含义 |
|---|---|
$PATH |
命令搜索路径 |
$HOME |
当前用户主目录 |
$USER / $UID |
当前用户名 / 用户ID |
$SHELL |
当前使用的 Shell 解释器路径 |
$PWD |
当前工作目录 |
$OLDPWD |
上一次工作目录(cd - 依赖此变量) |
$RANDOM |
每次读取时返回 0~32767 的随机整数 |
$LINENO |
当前脚本执行的行号(调试利器) |
$FUNCNAME |
当前函数名数组(仅 Bash) |
$BASH_VERSION |
当前 Bash 版本号 |
4.综合演示脚本
bash
[root@test shell]# vim special_vars_demo.sh
#!/bin/bash
# ============================================================
# Shell 特殊变量综合演示脚本
# 用法: ./special_vars_demo.sh arg1 "arg2 with space" arg3
# ============================================================
echo "========== 1. 位置参数变量 =========="
echo "\$0 (脚本名称) : $0"
echo "\$1 (第1个参数) : $1"
echo "\$2 (第2个参数) : $2"
echo "\$3 (第3个参数) : $3"
echo "\${10} (第10个参数) : ${10:-'(未传递)'}"
echo "\$# (参数总个数) : $#"
echo ""
echo "--- \$* 与 \$@ 在双引号中的关键区别 ---"
echo "\"\$*\" 合并为一个字符串:"
for item in "$*"; do
echo " -> [$item]"
done
echo "\"\$@\" 保持独立字符串:"
for item in "$@"; do
echo " -> [$item]"
done
echo ""
echo "========== 2. 状态与进程变量 =========="
# 先执行一个必然失败的命令来演示 $?
ls /this_path_does_not_exist &>/dev/null
FAIL_STATUS=$?
ls /tmp &>/dev/null
SUCCESS_STATUS=$?
echo "\$? (失败命令退出码) : $FAIL_STATUS (非0表示失败)"
echo "\$? (成功命令退出码) : $SUCCESS_STATUS (0表示成功)"
echo "\$\$ (当前Shell PID) : $$"
# 启动后台任务演示 $!
sleep 30 &
BG_PID=$!
echo "\$! (后台任务 PID) : $BG_PID"
kill $BG_PID 2>/dev/null # 清理后台进程
echo "\$_ (上条命令末参) : $_ (即 kill 命令的最后一个参数)"
echo ""
echo "========== 3. Shell 环境变量 =========="
echo "\$- (Shell选项标志) : $-"
echo "\$LINENO (当前行号) : $LINENO"
echo "\$RANDOM (随机数) : $RANDOM"
echo "\$PWD (工作目录) : $PWD"
echo "\$HOME (用户主目录) : $HOME"
echo "\$USER (当前用户) : $USER"
echo "\$SHELL (解释器) : $SHELL"
echo "\$BASH_VERSION : $BASH_VERSION"
echo ""
echo "========== 4. 函数内的特殊变量作用域 =========="
demo_func() {
echo " 函数内 \$0 : $0 (仍是脚本名,不是函数名)"
echo " 函数内 \$1 : $1 (是函数的参数,不是脚本的参数)"
echo " 函数内 \$# : $# (是函数的参数个数)"
echo " 函数内 FUNCNAME : ${FUNCNAME[0]}"
}
demo_func "func_arg1" "func_arg2"
5.结果解析
bash
[root@test shell]# sh special_vars_demo.sh hello "world foo" bar
========== 1. 位置参数变量 ==========
$0 (脚本名称) : special_vars_demo.sh
$1 (第1个参数) : hello
$2 (第2个参数) : world foo
$3 (第3个参数) : bar
${10} (第10个参数) : '(未传递)'
$# (参数总个数) : 3
--- $* 与 $@ 在双引号中的关键区别 ---
"$*" 合并为一个字符串:
-> [hello world foo bar]
"$@" 保持独立字符串:
-> [hello]
-> [world foo]
-> [bar]
========== 2. 状态与进程变量 ==========
$? (失败命令退出码) : 2 (非0表示失败)
$? (成功命令退出码) : 0 (0表示成功)
$$ (当前Shell PID) : 4859
$! (后台任务 PID) : 4862
$_ (上条命令末参) : 4862 (即 kill 命令的最后一个参数)
special_vars_demo.sh: line 47: 4862 Terminated sleep 30
========== 3. Shell 环境变量 ==========
$- (Shell选项标志) : hB
$LINENO (当前行号) : 51
$RANDOM (随机数) : 23275
$PWD (工作目录) : /opt/shell
$HOME (用户主目录) : /root
$USER (当前用户) : root
$SHELL (解释器) : /bin/bash
$BASH_VERSION : 4.2.46(2)-release
========== 4. 函数内的特殊变量作用域 ==========
函数内 $0 : special_vars_demo.sh (仍是脚本名,不是函数名)
函数内 $1 : func_arg1 (是函数的参数,不是脚本的参数)
函数内 $# : 2 (是函数的参数个数)
函数内 FUNCNAME : demo_func
6.核心避坑指南
$?极易丢失 :$?在每一条命令执行后都会被覆盖 。如果你需要检查某条命令的状态,必须在紧接其后立即用普通变量保存(如ret=$?),否则后续任何命令(包括echo)都会将其重置。- 遍历参数永远用
"$@":"$*"会将所有参数按IFS(默认空格)拼接成一个字符串,当参数本身包含空格时会导致严重的逻辑错误。for arg in "$@"是 Shell 编程的黄金法则。 - 函数会遮蔽位置参数 :进入函数后,
$1、$#、$@全部变为函数的参数,脚本原始参数被暂时隐藏。若需在函数中使用脚本参数,必须在调用函数前将其赋值给普通变量。 ${n}的花括号不可省略 :当n >= 10时,必须写成${10}。如果写成$10,Shell 会解析为$1后面紧跟字符0,产生完全错误的结果。- 可移植性注意 :
$FUNCNAME、$BASH_VERSION、$RANDOM是 Bash 特有 变量。如果脚本需要在sh/dash等 POSIX Shell 中运行,应避免使用这些变量或做好兼容判断。