欢迎来到罐子的实验室
本次目标
学会用变量存储和复用值,让命令「记住」东西;理解什么是环境变量,怎么把变量传给子进程。
学完你能做什么:
1. 为什么需要变量
你写过 cd ~/Documents/work/2024/q1/reports 这种长路径吗?每次都打一遍很烦。如果给这串路径起个名字 REPORTS,以后只要 cd $REPORTS 就行——这就是变量。
变量 = 给某个值起个名字,方便复用、修改。Shell 本身就是个迷你编程语言,变量是它的基础。
2. 读取变量:$
Shell 里有些预定义变量,比如 PWD 始终存着当前目录:
user@host:~$ echo $PWD/home/user
注意那个 $:变量名字是 PWD,要读取它的值就在前面加 $。这个「加 $ 取值」的动作叫变量展开(expansion)。
动手练习 1:看看常见变量
任务:打印这几个预定义变量的值:$USER(用户名)、$HOME(家目录)、$SHELL(当前 Shell)。
思路引导:echo $变量名。
参考答案:
user@host:~$ echo $USERuseruser@host:~$ echo $HOME/home/useruser@host:~$ echo $SHELL/bin/bash
常见错误:写 echo USER(少了 $)——打印的是字面的 "USER" 而不是用户名。
3. 设置变量:=
自己创建变量用等号赋值:
user@host:~$ GREETING=”hello world”user@host:~$ echo $GREETINGhello world
三条铁律
- 等号两边不能有空格!
VAR=1 对,VAR = 1 错(Shell 会以为你要运行一个叫 VAR 的命令)。 - 赋值时不加
$,只有读取时才加:VAR=1 而非 $VAR=1。 - 值含空格要用引号
user@host:~$ NAME=Aliceuser@host:~$ echo $NAMEAliceuser@host:~$ NAME = Alice# ❌ 等号有空格NAME: command not found# Shell 把 NAME 当成命令了
动手练习 2:设个常用路径
任务:把变量 WORK 设成你的工作目录路径(如 ~/linux-practice),然后用 cd $WORK 切过去。
参考答案:
user@host:~$ WORK=~/linux-practiceuser@host:~$ cd $WORKuser@host:~/linux-practice$ pwd/home/user/linux-practice
常见错误:
- ❌
WORK = ~/...(等号空格)→ 报错。 - ❌
$WORK=~/...(赋值加 $)→ 语法错误。
4. 引号:单引号 vs 双引号
值里有空格必须加引号。但单引号和双引号不一样:
user@host:~$ NAME=Aliceuser@host:~$ echo ”Hello $NAME”# 双引号:$NAME 被替换Hello Aliceuser@host:~$ echo 'Hello $NAME'# 单引号:原样输出Hello $NAME
经验法则:
- 想让内容完全照搬(如正则、含
$ 的字面文本)→ 用单引号。 - 任何时候,变量引用都建议加双引号
动手练习 3:体会引号
任务:
NAME=Alice,分别用双引号和单引号打印 I am $NAME,对比结果。
参考答案:
user@host:~$ NAME=Aliceuser@host:~$ echo ”I am $NAME”I am Aliceuser@host:~$ echo 'I am $NAME'I am $NAME
常见错误:echo $NAME is here 看似没事,但若 NAME="Alice Bob",Shell 会把 Alice 和 Bob 当两个词处理(词拆分)。安全写法:echo "$NAME is here"。
5. export:变成环境变量
你设的变量默认只在当前 Shell 进程里可见,启动的子进程继承不到:
user@host:~$ VAR=hellouser@host:~$ echo $VARhellouser@host:~$ bash# 启动一个子 Shelluser@host:~$ echo $VAR# 子 Shell 里看不到user@host:~$ exit# 退回原 Shelluser@host:~$ echo $VARhello
要让子进程也看到,必须 export(导出):
user@host:~$ VAR=hellouser@host:~$ export VAR# 或一步到位:export VAR=hellouser@host:~$ bashuser@host:~$ echo $VARhello# 子进程拿到了!
被导出的变量叫环境变量,它们构成了进程的「环境」。很多程序靠读环境变量来配置自己,比如:
PATH —— Shell 找命令的目录列表(→ 详见《模块 13》)HOMELANGEDITOR
查看所有环境变量
user@host:~$ env# 列出所有已导出的环境变量user@host:~$ printenv PATH# 查看单个变量(不展开,更可靠)
动手练习 4:导出并验证
任务:
- 不导出,启动
bash,确认子 Shell 里 $MY_VAR 为空。 - 退回,
export MY_VAR,再进 bash 验证能看到。
参考答案:
user@host:~$ MY_VAR=secretuser@host:~$ bash -c 'echo ”[$MY_VAR]”'# 子进程里[]user@host:~$ export MY_VARuser@host:~$ bash -c 'echo ”[$MY_VAR]”'[secret]
(用 bash -c '命令' 在子进程跑一条命令,省得手动进/退子 Shell。)
6. 命令替换:$()
把命令的输出赋给变量,用 $(命令):
user@host:~$ TODAY=$(date +%Y-%m-%d)user@host:~$ echo $TODAY2024-06-30user@host:~$ FILES=$(ls /etc | wc -l)user@host:~$ echo ”etc 下有 $FILES 项”etc 下有 243 项
$(...) 会先运行里面的命令,把输出塞回到当前位置。
💡 老式写法是反引号 `命令`,比如 TODAY=`date`。但反引号不能嵌套、转义麻烦,现代一律推荐 $()。
动手练习 5:备份文件名带日期
任务:把 todo.txt 复制成 todo-YYYYMMDD.txt(带今天日期)。
思路引导:先用 $(date +%Y%m%d) 拿到日期串,再拼进文件名。
参考答案:
user@host:~$ cp todo.txt ”todo-$(date +%Y%m%d).txt”user@host:~$ ls todo*todo-20240630.txt todo.txt
常见错误:cp todo.txt todo-$(date).txt——date 不带格式参数会输出带空格的串(Sun Jun 30 ...),文件名里有空格是大麻烦。加 +%Y%m%d 指定格式。
7. read:从输入读值
read 把一行输入读进变量。配合 -p 还能显示提示:
user@host:~$ read -p ”请输入你的名字: ” NAME请输入你的名字: Alice# 这行是你输入的user@host:~$ echo ”你好, $NAME”你好, Alice
read 默认从 stdin 读,所以可以配合输入重定向读文件:
user@host:~$ echo ”first line” > data.txtuser@host:~$ read LINE < data.txtuser@host:~$ echo ”$LINE”first line
注意 read 只读一行,要逐行处理整个文件得用循环(→ 详见《模块 11》)。
💡 read 是 Shell 内建命令(不是外部程序),所以它不受 PATH 影响——这点在《模块 13》和《模块 14》很有用。
动手练习 6:交互式输入
任务:写一行命令,提示用户输入年龄,然后输出「你 X 岁了」。
参考答案:
user@host:~$ read -p ”年龄: ” AGE && echo ”你 $AGE 岁了”年龄: 25你 25 岁了
(&& 表示前一条成功才执行后一条,详见《模块 11》。)
常见错误:read NAME 不加提示,用户不知道在等输入——养成用 -p 的习惯。
8. 特殊变量速览
Shell 有些「自带」的特殊变量,名字是符号:
| |
|---|
$? | 上一条命令的退出码(0=成功,非0=失败,→ 详见《模块 09》) |
$$ | |
$! | |
$0 | |
$1 | |
$# | |
$@ | |
user@host:~$ ls /不存在ls: cannot access '/不存在': No such file or directoryuser@host:~$ echo $?2# 失败,退出码非 0user@host:~$ echo hellohellouser@host:~$ echo $?0# 成功
本模块速查表
| |
|---|
| echo $VAR |
| VAR=value |
| VAR="a b c" |
| export VAR |
| VAR=$(命令) |
| env |
| read -p "提示" VAR |
| read VAR < 文件 |
引号对比
特殊变量
下一步
变量让命令能「记」值了。但每条命令都是一次性的——你想让命令长期运行、随时控制、按需停止,这就涉及进程管理。