跳到主要内容

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. 环境变量​

环境变量与 export:查看设置、子进程继承和导出标记

是什么​

环境变量就是系统里的「全局配置项」,一个名字对应一个值。程序启动时可以读取它们,从而不用把配置写死在代码里。

变量含义典型值
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

⚠️ 三个坑​

  1. 等号两边不能有空格:NAME = zzc 会被当成「执行 NAME 命令,参数是 = 和 zzc」,报 NAME: command not found。
  2. 单引号里的 $ 不会被替换:echo '$HOME' 原样输出 $HOME,echo "$HOME" 才输出 /home/zzc。
  3. export 过一次,就一直是导出的:之后再写 NAME=别的值,只改了值,子进程照样能读到。想让它变回「只有自己能看到」,要先 unset NAME 再重新赋值。用 declare -p NAME 可以检查:输出里有 -x 就表示已导出。

2. PATH —— 命令是怎么被找到的​

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 整个覆盖掉,之后 ls cat 全部 command not found(关掉终端重开就恢复了,别慌)。
  • 路径里用 $HOME 而不是 ~:~ 在双引号里不会被展开。

3. 让设置永久生效:~/.bashrc​

bashrc 与 source:配置备份、追加、重读,以及当前 shell 和子进程的区别

用 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 # ✅ 文件里原样是 $PATH
echo "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​

权限速查与排错:rwx、常见数字权限、文件与目录权限和 sudo 原则

三类人 × 三种权限​

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。后端最常见的几个:

数字字母用在哪
755rwxr-xr-x脚本、程序、目录:大家能用,只有自己能改
644rw-r--r--普通文件、配置:大家能看,只有自己能改
600rw-------密钥、密码文件:只有自己能读写,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课环境变量与权限