Shell中的特殊变量

在 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 记录并后续 waitkill
$- 当前 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$RANDOMBash 特有 变量。如果脚本需要在 sh/dash 等 POSIX Shell 中运行,应避免使用这些变量或做好兼容判断。
相关推荐
huainingning1 小时前
交换机通过ftp下载文件或者传输文件到ftp服务器
运维·服务器·网络
An_s2 小时前
机器学习python之识别图中物品信息
java·linux·开发语言
edwarddamon2 小时前
CentOS 7 国内安装 Docker CE(阿里云源)
运维
Championship.23.242 小时前
Linux 3.0 LVDS驱动开发详解
linux·运维·驱动开发·lvds
小池先生3 小时前
Windows服务器如何备份
运维·服务器
FII工业富联科技服务3 小时前
灯塔工厂用例详解:AR 远程巡检与智能运维闭环落地实践
运维·ar
mounter6254 小时前
走向长时运行:引入协程(Coroutines),打破 BPF 程序的“一堂到底”限制
linux·ebpf·kernel
六点_dn4 小时前
Linux学习笔记-shell运算符
linux·笔记·学习
hehelm5 小时前
IO 多路复用 — Reactor
linux·服务器·网络·数据库·c++