在 Shell 脚本中,$#, $?, $@, $1 以及其他以 $ 开头的特殊变量是位置参数和特殊参数,它们提供了关于脚本执行环境、传递给脚本的参数以及上一个命令执行状态的重要信息。
核心特殊变量详解
| 变量 | 名称 | 含义 |
|---|---|---|
$0 |
脚本名称 | 表示当前执行的脚本或程序的名称(包括路径,如果调用时包含路径)。 |
$1, $2, ..., $9 |
位置参数 | 表示传递给脚本或函数的第1个、第2个、...、第9个命令行参数。 |
${10} |
位置参数 (10+) | 当参数超过9个时,必须用 {} 括起来,如 ${10}, ${11} 等。 |
$# |
参数个数 | 表示传递给脚本或函数的位置参数的总数量。 |
$@ |
所有参数 (保留空格) | 表示传递给脚本或函数的所有位置参数。当用双引号括起来时 ("$@"),它会将每个参数视为单独的、带引号的字符串,保留了参数中的空格和特殊字符。这是遍历参数的推荐方式。 |
$* |
所有参数 (合并为一个) | 也表示所有位置参数。当用双引号括起来时 ("$*"),它会将所有参数合并成一个单一的字符串,参数之间用第一个字符(通常是空格)分隔。 |
$$ |
当前进程ID (PID) | 表示当前正在运行的 Shell 脚本的进程ID。常用于创建唯一的临时文件名。 |
$! |
最后一个后台进程ID | 表示最近一个在后台运行的进程的进程ID。例如:command & 后,$! 会保存 command 的 PID。 |
$? |
上一个命令的退出状态 | 表示上一个执行的命令或脚本的退出状态码。 |
* 0 表示成功。 |
||
* 非零值(通常是 1-255)表示失败或有错误。 |
||
$- |
当前 Shell 选项标志 | 显示当前 Shell 的启用选项(如 himBH)。 |
$_ |
上一个参数 | 表示上一个命令的最后一个参数。在脚本中,它通常表示 $0(脚本名),但在交互式 Shell 中很有用。 |
$IFS |
内部字段分隔符 | (Internal Field Separator) 一个特殊的环境变量,定义了 Shell 如何分隔单词。默认是空格、制表符和换行符。 |
详细示例
假设你有一个脚本 myscript.sh,并用以下命令执行它:
./myscript.sh "Hello World" file.txt 123
| 变量 | 在此命令下的值 | 说明 |
|---|---|---|
$0 |
./myscript.sh |
脚本名称 |
$1 |
Hello World |
第一个参数(带空格,因为用引号括起来了) |
$2 |
file.txt |
第二个参数 |
$3 |
123 |
第三个参数 |
$# |
3 |
总共有3个参数 |
$@ |
"Hello World" "file.txt" "123" |
所有参数,每个都保持原样(当用 "$@" 引用时) |
$* |
"Hello World file.txt 123" |
所有参数合并成一个字符串(当用 "$*" 引用时) |
$$ |
12345 |
假设脚本的 PID 是 12345 |
$? |
(取决于上一个命令) |
例如,如果上一个 ls 命令成功,$? 就是 0
|
$! |
(取决于后台进程) |
如果刚运行了 sleep 100 &,$! 就是 sleep 进程的 PID |
$@ vs $* 的关键区别 (用双引号时)
这是最容易混淆的地方:
#!/bin/bash
# 假设调用: script.sh "a b" c
echo "使用 \"\$@\":"
for arg in "$@"; do
echo "[$arg]"
done
# 输出:
# [a b] <- 作为一个整体
# [c]
echo "使用 \"\$*\":"
for arg in "$*"; do
echo "[$arg]"
done
# 输出:
# [a b c] <- 所有参数合并成一个字符串
结论:在 for 循环或传递参数时,几乎总是应该使用 "$@",因为它能正确处理包含空格的参数。
其他重要的 $ 相关表达式
除了上述特殊变量,Shell 还支持强大的参数扩展 (Parameter Expansion),这些也以 $ 开头:
| 表达式 | 含义 |
|---|---|
${var} |
基本变量引用,等同于 $var,但更安全,尤其在变量名后紧跟字符时。 |
${var:-default} |
如果 var 未定义或为空,则使用 default。 |
${var:=default} |
如果 var 未定义或为空,则设置 var 为 default 并使用它。 |
${var:+value} |
如果 var 已定义且非空,则使用 value,否则为空。 |
${var:?message} |
如果 var 未定义或为空,则打印 message 并退出脚本。 |
${#var} |
返回变量 var 的字符串长度。 |
${var#pattern} |
从 var 开头删除最短匹配 pattern 的部分。 |
${var##pattern} |
从 var 开头删除最长匹配 pattern 的部分。 |
${var%pattern} |
从 var 末尾删除最短匹配 pattern 的部分。 |
${var%%pattern} |
从 var 末尾删除最长匹配 pattern 的部分。 |
${var/pattern/replacement} |
将 var 中第一个匹配 pattern 的部分替换为 replacement。 |
${var//pattern/replacement} |
将 var 中所有匹配 pattern 的部分替换为 replacement。 |
总结
-
$1,$2, ...:获取具体的命令行参数。 -
$#:知道有多少个参数。 -
$@:安全地遍历或传递所有参数。 -
$?:检查上一个命令是否成功。 -
$$,$!:获取进程 ID。 - 参数扩展:提供强大的字符串操作能力。
理解这些 $ 变量和表达式是编写健壮 Shell 脚本的基础。