Linux 命令初识第 5 课:环境变量与权限
目标:会查看、设置环境变量,理解
PATH为什么决定「命令找不找得到」,会把变量永久写进~/.bashrc;看得懂ls -l里的rwxr-xr-x,会用chmod改权限,知道sudo什么时候用。 用时:约 40 分钟。全部在 Ubuntu 里敲。
这一课解决后端最常见的两类「环境问题」:
| 报错 | 原因 | 本课哪一节 |
|---|---|---|
go: command not found(明明装了) | PATH 里没有 go 的目录 | 第 2 节 |
| 程序读不到数据库密码 / 读到的是开发环境的配置 | 环境变量没设,或没 export | 第 1 节 |
./start.sh: Permission denied | 脚本没有执行权限 | 第 4 节 |
cat: /etc/shadow: Permission denied | 当前用户没有读权限 | 第 3、5 节 |
0. 准备练习数据
mkdir -p ~/practice/lesson5 && cd ~/practice/lesson5
printf '#!/bin/bash\necho "hello, $USER"\n' > hello.sh
printf 'DB_PASSWORD=fake-123456\n' > secret.env
ls -l
hello.sh 是一个只有两行的脚本:第一行 #!/bin/bash 告诉系统「用 bash 来执行我」,第二行打个招呼。secret.env 模拟一个存放密码的配置文件(内容是假的)。
1. 环境变量

是什么
环境变量就是系统里的「全局配置项」,一个名字对应一个值。程序启动时可以读取它们,从而不用把配置写死在代码里。
| 变量 | 含义 | 典型值 |
|---|---|---|
HOME | 当前用户的家目录 | /home/zzc |
USER | 当前用户名 | zzc |
PWD | 当前所在目录 | /home/zzc/practice/lesson5 |
SHELL | 当前用的 shell | /bin/bash |
PATH | 去哪些目录里找命令(第 2 节细讲) | /usr/local/bin:/usr/bin:/bin:... |
后端里环境变量用得非常多:
GOPATH=/home/zzc/go # Go 的工作目录
JAVA_HOME=/usr/lib/jvm/java-21 # Java 装在哪
APP_ENV=prod # 程序据此决定连开发库还是生产库
DB_PASSWORD=... # 密码不写进代码、不提交到 Git,而是放环境变量
查看
echo $HOME # 看一个:变量名前加 $ 表示「取它的值」
echo "我是 $USER,在 $PWD" # 可以嵌在字符串里(要用双引号)
env # 看全部环境变量
env | grep GO # 配合管道筛选,上节课的本事用上了
设置:普通变量 vs export
NAME=zzc # 设一个变量。⚠️ 等号两边不能有空格
echo $NAME # zzc
这样设的只是当前 shell 自己的变量。当前 shell 里启动的程序(子进程)看不到它。要让子进程也能看到,必须 export:
export NAME=zzc # 设置并导出,变成真正的「环境变量」
当前终端 (bash)
├─ NAME=zzc ← 只有自己能看到
├─ export APP_ENV=prod ← 自己和子进程都能看到
│
└──→ 启动的程序 (子进程:go run、java、脚本……)
能读到 APP_ENV,读不到 NAME
这就是后端常见的坑:在终端里设了
DB_PASSWORD=xxx,再启动程序,程序却说没读到——因为忘了export。
只对一条命令生效
APP_ENV=prod ./start.sh # 只在执行这一条命令时设置 APP_ENV,执行完就没了
删除
unset NAME
⚠️ 三个坑
- 等号两边不能有空格:
NAME = zzc会被当成「执行 NAME 命令,参数是=和zzc」,报NAME: command not found。 - 单引号里的
$不会被替换:echo '$HOME'原样输出$HOME,echo "$HOME"才输出/home/zzc。 - export 过一次,就一直是导出的:之后再写
NAME=别的值,只改了值,子进程照样能读到。想让它变回「只有自己能看到」,要先unset NAME再重新赋值。用declare -p NAME可以检查:输出里有-x就表示已导出。
2. PATH —— 命令是怎么被找到的

你敲 ls 的时候,系统并不知道 ls 在哪。它会按 PATH 里列出的目录,从左到右一个个去找有没有叫 ls 的程序,找到第一个就执行。
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:...
目录之间用 : 隔开。挤在一行不好看,可以用 tr 把 : 换成换行:
echo $PATH | tr ':' '\n' # tr 'A' 'B':把输入里的字符 A 替换成 B
敲 ls
│
├─ /usr/local/sbin/ls ? 没有
├─ /usr/local/bin/ls ? 没有
├─ /usr/sbin/ls ? 没有
├─ /usr/bin/ls ? 有!执行它 ← which ls 显示的就是这个
└─ (后面的不再找)
所以:
which ls就是在模拟这个查找过程,告诉你最终找到的是哪个。- 一个程序所在的目录不在
PATH里,就只能写完整路径(或./)去执行,否则报command not found。装完 Go、Java 后要「配置环境变量」,本质就是把它们的bin目录加进PATH。
往 PATH 里加目录
export PATH="$PATH:$HOME/practice/lesson5" # 在原来的 PATH 末尾加一个目录
- 一定要带上
$PATH:。写成export PATH=$HOME/practice/lesson5会把原来的 PATH 整个覆盖掉,之后lscat全部command not found(关掉终端重开就恢复了,别慌)。 - 路径里用
$HOME而不是~:~在双引号里不会被展开。
3. 让设置永久生效:~/.bashrc

用 export 设的变量,关掉终端就没了。每次打开终端,bash 都会自动执行一遍家目录下的 ~/.bashrc,所以把 export 写进这个文件,就相当于永久生效。
echo 'export GREETING=hi' >> ~/.bashrc # ⚠️ 一定是 >>,用 > 会把整个 .bashrc 清空
tail -n 3 ~/.bashrc # 确认写进去了
source ~/.bashrc # 让当前终端立即重新读一遍,不用重开
echo $GREETING
⚠️ 往
.bashrc里写带$的内容,echo外面必须用单引号:echo 'export PATH="$PATH:$HOME/practice/lesson5"' >> ~/.bashrc # ✅ 文件里原样是 $PATHecho "export PATH=$PATH:$HOME/practice/lesson5" >> ~/.bashrc # ❌ $PATH 当场被替换,写进去一长串写死的路径echo "export PATH="$PATH:$HOME/practice/lesson5"" >> ~/.bashrc # ❌ 双引号不能套双引号,效果同上写完一定
tail -n 1 ~/.bashrc看一眼,文件里应该还能看到$PATH这几个字。
| 方式 | 生效范围 | 关掉终端后 |
|---|---|---|
NAME=value | 当前 shell | 没了 |
export NAME=value | 当前 shell + 它启动的程序 | 没了 |
写进 ~/.bashrc 并 source | 以后每个新终端 | 一直在 |
第 2 课学过
.开头的是隐藏文件,所以ls看不到.bashrc,要ls -a。
4. 权限 —— 读懂 rwxr-xr-x

三类人 × 三种权限
ls -l 第一列,比如 -rwxr-xr--,拆开看:
- rwx r-x r--
↑ ↑ ↑ ↑
类型 属主 属组 其他人
(user) (group) (others)
- 类型:
-普通文件,d目录,l链接。 - 后面 9 个字符每 3 个一组,分别是文件主人、同组用户、其他所有人的权限。
ls -l第 3、4 列就是属主和属组的名字。
| 字母 | 对文件 | 对目录 |
|---|---|---|
r 读 | 能看内容(cat) | 能列出里面有什么(ls) |
w 写 | 能改内容 | 能在里面新建、删除文件 |
x 执行 | 能当程序运行 | 能 cd 进去 |
- | 没有这项权限 | 同左 |
数字表示法
每种权限对应一个数字,一组里加起来:
r = 4 w = 2 x = 1
rwx = 4+2+1 = 7
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
rw- = 4+2+0 = 6
所以 rwxr-xr-x = 755,rw-r--r-- = 644。后端最常见的几个:
| 数字 | 字母 | 用在哪 |
|---|---|---|
755 | rwxr-xr-x | 脚本、程序、目录:大家能用,只有自己能改 |
644 | rw-r--r-- | 普通文件、配置:大家能看,只有自己能改 |
600 | rw------- | 密钥、密码文件:只有自己能读写,SSH 私钥必须是这个 |
chmod —— 改权限
chmod +x hello.sh # 给所有人加上执行权限(最常用)
chmod u+x hello.sh # 只给属主加(u=属主 g=属组 o=其他人 a=所有人)
chmod o-r secret.env # 去掉其他人的读权限
chmod 755 hello.sh # 数字写法,直接设成 rwxr-xr-x
chmod 600 secret.env # 密码文件只给自己
为什么脚本要 ./ 加执行权限才能跑
./hello.sh # 没有 x 权限 → Permission denied
chmod +x hello.sh
./hello.sh # hello, zzc
hello.sh # command not found ← 当前目录不在 PATH 里(第 2 节)
./ 的意思是「执行当前目录下的 hello.sh」,相当于给了完整路径,所以不用去 PATH 里找。
了解即可:
bash hello.sh不需要 x 权限也能跑,因为这时被执行的是 bash,hello.sh 只是它读取的文件。
5. root 与 sudo
root是 Linux 的超级管理员,权限检查对它无效,什么都能读、能改、能删。- 普通用户临时借用 root 权限,在命令前加
sudo,会要求输入你自己的密码(输入时屏幕上不显示,正常现象)。
whoami # 我是谁
cat /etc/shadow # Permission denied:存放密码哈希的文件,只有 root 能读
ls -l /etc/shadow # 看看它的权限
sudo cat /etc/shadow # 可以了
sudo apt install tree # 装软件要改系统目录,所以要 sudo
了解即可:
chown改文件的属主,如sudo chown zzc:zzc file,部署时遇到「文件是 root 的,程序读不了」会用到。
⚠️ sudo 的原则
- 只在必须的时候用(装软件、改系统配置),不要遇到报错就无脑加 sudo。
- 报
Permission denied时先ls -l看清是谁的文件、缺什么权限,再决定是chmod、换用户,还是真的需要 sudo。 sudo rm -rf删错了没有任何保护,第 2 课的原则在这里要加倍小心。- 不要随手
chmod 777(所有人都能读写执行)。它能「解决」权限报错,但等于把门拆了。
6. 跟着敲一遍
cd ~/practice/lesson5
echo $HOME $USER
echo '$HOME' "$HOME" # 猜:两边输出一样吗?
env | wc -l
echo $PATH | tr ':' '\n'
NAME=zzc
echo $NAME
bash -c 'echo "子进程看到: $NAME"' # 猜:能看到吗?(bash -c 会启动一个新的 bash 子进程)
export NAME
bash -c 'echo "子进程看到: $NAME"' # 猜:现在呢?
ls -l hello.sh secret.env
./hello.sh # 猜:能跑吗?
chmod +x hello.sh
ls -l hello.sh # 第一列哪里变了?
./hello.sh
hello.sh # 猜:能跑吗?
chmod 600 secret.env
ls -l secret.env
cat /etc/shadow
ls -l /etc/shadow /etc/passwd # 对比两者的权限
每敲一条,先猜结果,再看输出对不对。
7. 练习题
题目在本文件夹的 题目解答.md 里,把每题的命令、输出和回答写在 解答: 下面,做完告诉我,我来批改。
8. 课后
- 提交:
01-环境准备-01-linux命令初识-第5课环境变量与权限