一、Linux 文件系统的核心哲学
一切皆文件
Linux 最根本的设计思想:系统中的一切——硬盘、目录、进程、键盘、网络连接——在操作系统眼里都是"文件"。
/dev/sda ← 你的硬盘,是个文件
/proc/cpuinfo ← CPU 信息,是个文件
/etc/passwd ← 用户账号,是个文件
/home/renbudang/.ssh/id_ed25519 ← SSH 私钥,是个文件
这意味着同一套读、写、移动的操作可以作用于任何对象。你 cat /proc/cpuinfo 就能读 CPU 信息,和读一个 txt 文件没有区别。
单根树结构
Windows 的世界里,每个分区是一个独立的起点(C: / D: / E:)。Linux 的世界里,只有一个根 /,所有的硬盘、U 盘、网络存储都"挂载"到这棵树的某个分支上。
/ ← 根,一切从这里开始
├── bin/ ← 系统基础命令(ls、cp、mv 等)
├── boot/ ← 启动相关文件(内核)
├── dev/ ← 设备文件(硬盘、键盘、终端)
├── etc/ ← 配置文件(Nginx、SSH、用户信息)
├── home/ ← 普通用户的家目录
│ └── renbudang/ ← 你的家目录(~ 就指向这里)
├── tmp/ ← 临时文件(重启后清空,不保证保留)
├── var/ ← 变化的数据(日志、网站、数据库)
│ └── www/html/ ← Nginx 默认网站目录
├── proc/ ← 进程和内核信息(虚拟文件系统,不占磁盘)
└── usr/ ← 用户安装的软件
路径:绝对 vs 相对
# 绝对路径——从根 / 出发
ls /home/renbudang/myblog/config.yml
# 相对路径——从当前位置出发
# 假设已经在 /home/renbudang 目录下:
ls myblog/config.yml # 等价于上面那条
ls ./myblog/config.yml # . 代表"当前目录",等价
ls ../ubuntu/.ssh # .. 代表"上一级目录",这里等于 /home/ubuntu/.ssh
ls ~/myblog # ~ 代表"当前用户的家目录"
判断规则:第一个字符是不是 /。
权限三元组
$ ls -l
-rw-rw-r-- 1 renbudang renbudang 6406 Jul 29 13:52 config.yml
│ │ │
│ │ └── 其他人的权限(r-- = 只能读)
│ └── 所属组的权限(rw- = 读写,不能执行)
└── 文件所有者的权限(rw- = 读写,不能执行)
类型标记:
- 普通文件
d 目录
l 符号链接(快捷方式)
权限字符:
r read 读(4)
w write 写(2)
x execute 执行(1)
- 没这个权限
rw- = 4+2+0 = 6。所以 chmod 644 file = 所有者读写(6),组只读(4),其他人只读(4)。
二、导航类命令
pwd — 我在哪
pwd
# /home/renbudang/myblog
Print Working Directory。告诉你当前在文件树的哪个节点。迷路了就敲一下。
cd — 去哪
cd /var/log # 去绝对路径
cd .. # 回上一级
cd ../.. # 回上两级
cd ~ # 回家目录
cd - # 回上一次在的目录(在 A 和 B 之间反复跳)
cd # 不带参数 = cd ~
ls — 这有什么
ls # 列出当前目录下的文件和子目录
ls -l # 长格式:权限、大小、时间
ls -lh # 长格式 + 人类可读的大小(1K、234M、2G)
ls -a # 显示隐藏文件(以 . 开头的文件)
ls -A # 同 -a,但不显示 . 和 .. 两个特殊目录项
ls /var/log # 列出指定目录的内容
-l 和 -h 可以组合:ls -lh。
ll
大多数 Linux 发行版把 ll 设为 ls -l 的别名。你敲 ll,实际执行的是 ls -l。
三、查看类命令
cat — 把文件内容打印到屏幕
cat config.yml # 整个文件一次打印
cat /proc/cpuinfo # 查看 CPU 信息
cat file1.md file2.md > merged.md # 合并两个文件
head / tail — 只看头或尾
head -20 config.yml # 前 20 行
tail -20 /var/log/nginx/error.log # 最后 20 行(查日志最常用)
tail -f /var/log/nginx/access.log # 实时跟踪日志新增内容(Ctrl+C 退出)
四、创建与删除
touch — 创建空文件或更新文件时间戳
touch newfile.md # 创建一个空的 md 文件
touch existingfile.md # 如果文件已存在,只更新它的"最后修改时间"
mkdir — 创建目录
mkdir mydir # 创建一个目录
mkdir -p a/b/c/d # 一口气创建嵌套的四个目录(不用逐层创建)
rm — 删除文件或目录
rm file.md # 删文件
rm -r mydir # 删目录(递归删除里面的所有内容)
rm -rf mydir # 强制删,不提示确认
rm -rf /var/www/html/* # 删 html 目录里的所有内容,但保留 html 目录本身
危险区:
rm -rf / var/www/html # ← / 后面有个空格!系统会先删 /(整个系统),再删指定目录
rm -rf * # ← 删掉当前目录下所有东西
永远在敲 rm -rf 前停一秒,确认路径。
cp — 复制
cp source.txt dest.txt # 复制文件
cp -r source_dir dest_dir # 复制目录(-r = 递归)
cp file1 file2 dest_dir/ # 复制多个文件到目标目录
mv — 移动或重命名
mv oldname.txt newname.txt # 重命名
mv file.txt /tmp/ # 移动到 /tmp
mv olddir newdir # 目录重命名(不需要 -r)
Linux 里没有单独的"重命名"命令——mv 同时干移动和重命名两件事。本质上,“重命名"就是"把文件从原路径移动到新路径”。
五、终端快捷键(不产生文件)
Tab — 自动补全
ls /home/r<Tab> # 自动补全成 ls /home/renbudang/(如果唯一匹配)
如果多个匹配,按两下 Tab 列出所有可能性:
ls /usr/<Tab><Tab>
# bin/ games/ include/ lib/ local/ sbin/ share/ src/
Ctrl+C — 终止当前运行的程序
hugo server # 启动后就卡在终端里了
# 按 Ctrl+C → 程序停止,回到命令提示符
不是"撤销",是"强制停止正在跑的程序"。
Ctrl+U — 清掉当前行
$ rm -rf /etc/nginx/很长的路径打错了想重来
# 按 Ctrl+U → 整行清空,光标回到行首
比按住 Backspace 删半天快得多。
六、命令组合模式
管道 | — 把一个命令的输出喂给另一个命令
ls -l | grep config # ls 的输出 → 只保留包含 "config" 的行
cat access.log | wc -l # 统计日志有多少行
docker ps | grep dvwa # 在运行中容器里找 dvwa
重定向 > / >> — 把输出写到文件
ls -l > filelist.txt # 覆盖写入
ls -l >> filelist.txt # 追加写入(不覆盖原有内容)
echo "hello" > new.txt # 创建文件并写入一行
七、实践建议
# 日常最常用的五个命令:
ls -lh # 看看这都有啥
cd 路径 # 换个地方
cat 文件名 # 快速看一眼文件内容
mv A B # 移动或改名
rm 文件 # 删除
# 遇到问题时的自救三步:
pwd # 先确认自己在哪
ls -la # 看看当前目录有什么(含隐藏文件)
history # 看看刚才敲了什么命令
八、rm -rf /* vs rm -rf * — 两个危险命令的区别
rm -rf /* — 从根目录开始删
/* = 根目录下的所有东西
= /bin + /boot + /dev + /etc + /home + /tmp + /var + /usr + ...
rm -rf /* → 从根 / 出发,遍历所有子目录,全部递归强制删除
→ 内核还在内存里跑,但磁盘上的系统文件全没了
→ 机器不会立刻死,但任何新的命令、读写操作都会失败
→ 这就是"删库跑路"的原型
rm -rf * — 从当前目录开始删
* = 当前目录下的所有东西(不包含隐藏文件)
假设你在 /home/renbudang/ 下执行:
rm -rf * → 删掉 myblog/、各类配置文件、所有可见文件
→ /home/renbudang/ 变成空目录
→ 系统文件(/bin、/etc 等)完好无损
核心区别
rm -rf /* |
rm -rf * |
|
|---|---|---|
| 起删点 | 根目录 / |
当前目录 |
| 影响范围 | 整个系统 | 当前目录下的可见内容 |
| 星号含义 | 根目录下的所有内容 | 当前目录下的所有内容 |
| 跟正则有关吗 | 无关——* 是 shell 的通配符(globbing),不是正则表达式 |
九、rm -rf .* 为什么危险
.* 展开后包含 ..:
当前你在 /home/renbudang/myblog/
Shell 把 .* 展开:
. ← 当前目录 /home/renbudang/myblog/
.. ← 上一级目录 /home/renbudang/
.gitignore ← 隐藏文件
rm -rf .. 会导致递归删除上一级目录 /home/renbudang/。现代 GNU rm 会拒绝删除 . 和 ..,但不应依赖这个保护——老系统、某些 Docker 镜像里的 rm 可能没有这个检查。
安全的删除隐藏文件写法:
rm -rf .[!.]* # 以 . 开头,第二位不能是 .,排除了 . 和 ..
十、Shell 通配规则(Globbing)
Shell 在把命令交给 rm(或 ls、cp)之前,自己先做文件名展开。这个过程叫 globbing,*、?、[...] 叫 glob pattern,不是正则表达式。
三条基本运算符
| 运算符 | 匹配什么 | 例子 | 展开结果 |
|---|---|---|---|
* |
任意长度的任意字符(除了以 . 开头) |
*.md |
a.md、config.md,不匹配 .hidden.md |
? |
任意一个字符 | file?.txt |
file1.txt、fileA.txt,不匹配 file12.txt |
[...] |
方括号里的一个字符 | file[0-9].txt |
file1.txt、file5.txt,不匹配 fileA.txt |
* 的两条硬规则
规则一:* 匹配任意字符序列,长度可以为 0
规则二:* 不会穿过 /(不能跨目录层级匹配)
规则三:以 . 开头的文件名不会被 * 匹配(这是有意为之的设计约定)
为什么 * 不匹配隐藏文件
这是 Unix 设计者有意为之的约定,不是技术限制。每个目录里必然存在 . 和 .. 两个特殊目录项,设计者想让 ls 和通配符默认忽略它们,于是定了一条规则:以 . 开头的文件名是"隐藏"的,* 不匹配它们。
四十年来所有类 Unix 系统都遵守这个约定。
.* 展开规则
.* 就是一个字面点号 . 后面跟一个 *:
. ← 固定的、字面的点号(文件名第一个字符必须是 .)
* ← 后面跟任意字符序列(包括空)
展开逻辑:在当前目录下找所有以 . 开头的名字。 但 . 和 .. 恰好也以点开头——所以它们也被包括在内。
echo .*
# 展开为:echo . .. .bashrc .gitconfig .local .ssh
. 在 glob 里只是一个普通字面字符,没有任何特殊含义。它不像正则表达式里 . 代表"任意字符"。
展开时机
Shell 在敲回车之后、命令启动之前完成展开。
ls *.md
→ Shell 先:把 *.md 替换成当前目录下所有匹配的文件名
→ Shell 执行:ls a.md config.md readme.md
→ ls 收到的参数里已经看不到 * 了——星号是 shell 的事,跟 ls 无关
常用通配组合速查
| 模式 | 匹配 | 危险程度 |
|---|---|---|
* |
所有非隐藏文件 | 🟢 安全 |
.* |
所有隐藏文件 + . + .. |
🔴 危险(含 ..) |
.[!.]* |
所有隐藏文件,排除 . 和 .. |
🟢 安全 |
*.* |
文件名里必须有 .(file.txt ✓,Makefile ✗) |
🟢 安全 |
*.{md,yaml} |
brace expansion 先展开为 *.md *.yaml |
🟢 安全 |
/* |
根目录下所有内容 | 🔴🔴🔴 致命 |
下一篇:Linux 文件编辑(vim / nano / VS Code Remote)与文件搜索(find / grep)