Shell 变量进阶知识详解
适合 Linux 运维、开发人员,涵盖特殊变量、内置命令、shift/eval 等高级用法。
一、位置参数变量(特殊变量)
在 Shell 脚本中,有一类变量由系统自动赋值,用于获取脚本运行时传入的参数、脚本自身信息等,称为位置参数变量。
1.1 常用位置参数列表
| 变量 | 含义 |
|---|---|
$0 | 当前脚本的文件名(如果执行时带路径,则包含路径) |
$1 ~ $9 | 第 1 到第 9 个位置参数,${10} 表示第 10 个(必须用大括号) |
$# | 位置参数的个数(不包括 $0) |
$* | 所有位置参数(作为一个整体字符串) |
$@ | 所有位置参数(每个作为独立字符串) |
$? | 上一条命令的退出状态码(0 成功,非 0 失败) |
$$ | 当前 Shell 进程的 PID |
$! | 最后一个后台进程的 PID |
$_ | 上一条命令的最后一个参数 |
1.2 基本用法示例
脚本 showargs.sh:
bash
#!/bin/bash
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\${10} = ${10}"
echo "\$# = $#"
echo "\$* = $*"
echo "\$@ = $@"
运行并传入 1~20 个参数:
bash
bash showargs.sh {1..20}
输出(部分省略):
text
$0 = showargs.sh
$1 = 1
$2 = 2
${10} = 10
$# = 20
$* = 1 2 3 ... 20
$@ = 1 2 3 ... 20
注意: 超过 9 的参数必须用
${10},不能写成$10(会被解析为$1后跟字符0)。
1.3 $* 和 $@ 的关键区别(重点)
- 不加双引号时:两者完全一样,都是将所有参数展开为多个单词。
- 加双引号时:
"$*":将所有参数合并为一个字符串,各参数之间由第一个字符(通常是空格)分隔。"$@":将每个参数都视为独立的字符串,保留参数中的空格和特殊字符。
示例脚本 test.sh:
bash
#!/bin/bash
echo "Using \"\$*\":"
for arg in "$*"; do
echo " $arg"
done
echo "Using \"\$@\":"
for arg in "$@"; do
echo " $arg"
done
运行:
bash
bash test.sh "hello world" "foo bar"
输出:
text
Using "$*":
hello world foo bar
Using "$@":
hello world
foo bar
推荐: 在需要原样传递参数给其他命令时,优先使用
"$@",它会保留每个参数的空格和引号。
1.4 其他特殊变量
$? – 上一条命令的退出状态
bash
pwd
echo $? # 0
pwa
echo $? # 127 (命令未找到)
$$ – 当前 Shell PID
bash
echo $$ # 例如 55654
kill -9 $$ # 杀死当前 Shell(慎用)
$! – 最后一个后台进程的 PID
bash
md5sum /dev/zero &
echo $! # 打印后台进程的 PID
$_ – 上一条命令的最后一个参数
bash
ls /etc/hosts /etc/fstab /etc/hostname
cat $_ # 等价于 cat /etc/hostname
1.5 实战:用位置参数简化系统管理
创建 /usr/bin/ssh_ctl 脚本:
bash
#!/bin/bash
systemctl $1 sshd
赋予执行权限:
bash
chmod +x /usr/bin/ssh_ctl
使用:
bash
ssh_ctl stop
ssh_ctl status
ssh_ctl start
这样通过传入 stop/start/status 作为 $1,即可快速控制 sshd 服务。
二、Shell 内置命令详解
Bash 内置了许多常用命令,它们不依赖外部程序,执行效率高。下面重点介绍 echo、eval、exec、read、shift。
2.1 echo – 输出(带转义和换行控制)
选项:
-n:输出后不换行-e:启用转义字符(默认不解析)
常用转义字符:
| 字符 | 含义 |
|---|---|
\n | 换行 |
\t | 水平制表符(Tab) |
\b | 退格(光标左移一格,若后面有字符则覆盖) |
示例:
bash
echo -n "hello "; echo "world" # 输出:hello world(不换行)
echo -e "line1\nline2" # 两行
echo -e "a\tb" # a b
echo -e "1\b23" # 输出 23(退格覆盖1)
echo -e "123\b" # 输出 123(末尾退格无覆盖,看不出效果)
退格原理详解:
\b只移动光标,不删除字符。- 只有后续有输出字符时,才会覆盖光标所在位置的字符。
- 末尾的
\b仅移动光标,不影响显示,所以视觉上无变化。
2.2 eval – 二次解析执行
eval 会将参数组合成一条新命令,然后再次交给 Shell 解析并执行。常用于动态生成命令。
示例1:输出最后一个位置参数
创建 neoval.sh:
bash
#!/bin/bash
eval "echo \$$#"
执行:
bash
bash neoval.sh hello world
# 输出:world
执行过程分解:
- Shell 首先解析双引号内的内容:
\$转义为普通字符$$#被解析为参数个数(这里是 2)- 得到字符串:
echo $2
eval将echo $2当作新命令执行,$2被替换为world,最终输出world。
示例2:动态设置环境变量
bash
eval $(ssh-agent) # 捕获 ssh-agent 的输出并执行其中的 export 命令
这相当于在当前 Shell 中设置了 SSH_AUTH_SOCK 和 SSH_AGENT_PID 环境变量。
2.3 read – 从标准输入读取
read 用于从键盘(或重定向)读取用户输入,并赋值给变量。
常用选项:
-p "提示文字":输出提示信息,等待输入-s:静默模式(不显示输入内容,适合密码)-t 秒数:超时时间
示例:
bash
#!/bin/sh
read -p "输入你喜欢的人:" str
echo "我猜你喜欢的人是:$str"
运行效果:
text
输入你喜欢的人:刘亦菲
我猜你喜欢的人是:刘亦菲
进阶用法: 从文件读取
bash
exec < /tmp/seq.log # 将标准输入重定向到文件
while read line; do
echo $line
done
2.4 exec – 替换当前 Shell 进程
exec 不创建子进程,而是用指定命令替换当前 Shell 进程。当命令执行完毕,该 Shell 进程也就终止了。
示例1:
bash
exec sleep 10 # 当前 Shell 变成 sleep 进程,10秒后退出
示例2:重定向标准输入
bash
#!/bin/bash
seq 5 > /tmp/seq.log
exec < /tmp/seq.log # 将 stdin 重定向到文件
while read line; do
echo $line
done
执行后输出 1 2 3 4 5(每行一个数字)。
2.5 shift – 位置参数左移
shift 使所有位置参数整体向左移动一位,$1 被丢弃,$2 变成新的 $1,$3 变成 $2……同时 $# 减 1。也可以指定移动位数,如 shift 2。
示例1:基本移动
bash
#!/bin/sh
echo $1 # 输出第一个参数
shift # 左移一位
echo $1 # 输出原来的第二个参数
运行:
bash
bash shift.sh hello world
# 输出:
hello
world
示例2:处理选项参数(如 -c)
bash
#!/bin/sh
if [ "$1" = "-c" ]; then
shift # 丢弃 -c,使 $2 变成新的 $1
fi
command="$1"
echo "准备执行命令:$command"
$command
运行:
bash
bash shift.sh -c pwd
# 输出:
准备执行命令:pwd
/root
如果用户只输入
bash shift.sh -c而未提供命令,$1为空,建议增加健壮性检查。
示例3:一次性左移多位
bash
#!/bin/sh
echo $1
shift 2 # 左移 2 位
echo $1
bash
bash shift.sh a b c d
# 输出:
a
c
三、综合实战:eval + shift 结合解析选项
虽然本笔记未涉及,但你可以扩展:通过循环和 shift 解析多个选项(如 -f file -v),再配合 eval 动态设置变量。
总结
| 知识点 | 关键点 |
|---|---|
| 位置参数 | $0~$9,${10},$#,$*,$@ |
$* 与 $@ | 加引号时,"$*" 为整体字符串,"$@" 为独立参数 |
| 特殊状态 | $?(退出码),$$(PID),$!(后台 PID),$_(最后参数) |
echo | -n 不换行,-e 转义,\b 退格需后续字符覆盖 |
eval | 二次解析,动态执行命令 |
read | 读取输入,-p 提示,-s 静默 |
exec | 替换当前进程或重定向 IO |
shift | 左移位置参数,支持 shift n |

1236

被折叠的 条评论
为什么被折叠?



