Chapter 02

文件系统导航

掌握 Linux 文件系统的导航技巧——ls、cd、find 命令的完整用法,以及高效的 Shell 操作技巧

基础导航命令

pwd(Print Working Directory)
打印当前工作目录的绝对路径。每个进程(包括 Shell)都有一个当前工作目录,cd 命令改变这个目录,相对路径总是相对于当前工作目录解析。
cd(Change Directory)
切换当前工作目录。Linux 文件系统是单一树状结构,所有目录都从 / 开始,cd 就是在这棵树上移动位置。
ls(List)
列出目录内容。ls 读取目录文件中的目录项(文件名+inode 编号映射),并根据 inode 信息显示文件属性。默认按字母序排列,隐藏文件(.开头)不显示。
# pwd:显示当前工作目录
pwd
# /home/alice/projects

# cd:切换目录
cd /home/alice           # 绝对路径:从根目录开始
cd projects              # 相对路径:相对于当前目录
cd ..                    # 上级目录(.. 是指向父目录的特殊目录项)
cd ../..                 # 上上级目录
cd ~                     # 主目录(等同于 cd $HOME,即 /home/当前用户名)
cd -                     # 返回上一次所在目录(交替两个目录工作时非常实用!)
cd                       # 不加参数 = 回到主目录(等同于 cd ~)

# ls:列出目录内容
ls                       # 简单列表(不含隐藏文件)
ls -l                    # 详细信息(长格式):权限、链接数、所有者、大小、时间、名称
ls -a                    # 显示隐藏文件(以 . 开头,包括 . 和 ..)
ls -la                   # 详细信息 + 隐藏文件(最常用组合)
ls -lh                   # 人类可读的文件大小(KB/MB/GB,而非字节数)
ls -lt                   # 按修改时间排序(最新在前)
ls -ltr                  # 按修改时间排序(最旧在前,查看日志时有用)
ls -lS                   # 按文件大小排序(最大在前)
ls -R                    # 递归列出所有子目录(大目录慎用,输出量大)
ls *.py                  # 通配符:只列出 .py 文件
ls -d */                 # 只列出目录(不显示目录内容)
ls -1                    # 每行一个文件(方便管道处理)
ls --color=auto          # 彩色输出(大多数发行版已默认启用)

解读 ls -l 输出格式

-rw-r--r--  1  alice  staff  4096  Jan 15 10:30  README.md
drwxr-xr-x  3  alice  staff    96  Jan 14 09:00  src/
lrwxrwxrwx  1  alice  staff    10  Jan 10 08:00  link -> target

列说明:
[1] 文件类型+权限(10位)
    第1位:- 普通文件,d 目录,l 软链接,b 块设备,c 字符设备,p 管道,s socket
    后9位:所有者权限(rwx) + 组权限(rwx) + 其他人权限(rwx)
[2] 硬链接计数(目录为 2+,文件通常为 1)
[3] 所有者用户名
[4] 所属组名
[5] 文件大小(字节,-lh 时显示 KB/MB)
[6][7] 最后修改时间
[8] 文件名(软链接显示 -> 目标)
文件类型字符
- 普通文件,d 目录(directory),l 符号链接(symlink),b 块设备(磁盘分区),c 字符设备(键盘/终端),p 命名管道(FIFO),s Socket 文件(进程间通信)
目录的链接计数含义
空目录至少有 2 个链接:① 父目录中的目录项(名称 → inode);② 目录内的 . 自引用。每增加一个子目录,父目录的链接计数加 1(因为子目录的 .. 指向父目录)。

路径表示方式

绝对路径 vs 相对路径

绝对路径(Absolute Path)

从根目录 / 开始,完整描述文件位置,与当前目录无关。

/home/alice/projects/app.py
/etc/nginx/nginx.conf
/var/log/syslog
/usr/bin/python3

优点:明确无歧义,脚本中推荐使用。

相对路径(Relative Path)

相对于当前工作目录,不以 / 开头。

projects/app.py       # 当前目录的子目录
../config/app.yaml    # 父目录的子目录
./run.sh              # 当前目录(./ 可省略但更清晰)
../../lib/utils.py    # 上上级目录的子目录

优点:简短,相对路径更灵活。

脚本中使用相对路径的风险

脚本中使用相对路径时,路径是相对于执行脚本时的工作目录,不是脚本文件所在目录。如果用户在不同目录下运行脚本,相对路径指向的位置会不同,导致找不到文件。脚本中应使用 $(dirname "$0") 获取脚本所在目录,再拼接相对路径,或始终使用绝对路径。

tree 命令:可视化目录结构

# 安装 tree
sudo apt install tree    # Ubuntu/Debian
sudo dnf install tree    # Fedora/RHEL
sudo pacman -S tree      # Arch Linux

# tree 常用选项
tree                         # 当前目录的树形结构(默认无深度限制)
tree /etc/nginx              # 指定目录
tree -L 2                    # 只显示 2 层深度(防止输出过多)
tree -L 3 -d                 # 3 层深度,只显示目录(不显示文件)
tree -a                      # 包含隐藏文件(以 . 开头)
tree -h                      # 显示文件大小(人类可读)
tree -I "node_modules|.git|__pycache__"  # 排除指定目录(支持正则)
tree --du -h                 # 显示各目录的累计大小(disk usage)
tree -o structure.txt        # 将输出保存到文件(生成项目文档时有用)
tree -J                      # 输出 JSON 格式(机器可读)

# 输出示例:
# project/
# ├── README.md
# ├── src/
# │   ├── main.py
# │   └── utils.py
# └── tests/
#     └── test_main.py
# 2 directories, 3 files

find 命令:强大的文件搜索

find 的工作原理

find 会递归遍历指定目录下的所有文件系统条目,对每个条目测试所有指定的条件(-name、-type、-mtime 等),满足所有条件的条目执行指定动作(默认 -print,即打印路径)。

语法:find [搜索路径] [条件...] [动作]

# ─────── 按名称搜索 ─────────────────────────────
find /home -name "*.log"           # 在 /home 下搜索所有 .log 文件
find . -name "config.yaml"         # 当前目录下名为 config.yaml 的文件
find / -iname "readme.md"          # 大小写不敏感搜索(-iname)
find . -name "*.py" -not -name "*test*"  # 排除含 test 的文件
find . -name "*.py" -o -name "*.ts"      # -o = OR:多个名称条件

# ─────── 按类型搜索 ─────────────────────────────
find /etc -type f                  # 只搜索普通文件(f=file)
find /tmp -type d                  # 只搜索目录(d=directory)
find /dev -type l                  # 只搜索符号链接(l=link)
find /dev -type b                  # 块设备文件(磁盘分区)

# ─────── 按时间搜索 ─────────────────────────────
# -mtime n:内容修改时间(-n = n天内,+n = n天前,n = 恰好n天前)
# -atime n:访问时间,-ctime n:状态变更时间
find /var/log -mtime -7            # 7天内修改过的文件
find /tmp -atime +30               # 30天前最后访问的文件(可以安全删除)
find . -newer reference.txt        # 比 reference.txt 更新的文件
find . -mmin -60                   # 60分钟内修改的文件(-mmin 分钟)

# ─────── 按大小搜索 ─────────────────────────────
find / -size +100M                 # 大于 100MB 的文件(k/M/G 为单位)
find . -size -1k                   # 小于 1KB 的文件
find . -size +10M -size -100M      # 10MB 到 100MB 之间的文件
find . -empty                      # 空文件或空目录

# ─────── 按权限/所有者搜索 ─────────────────────
find / -perm 777                   # 权限恰好为 777 的文件(安全审计)
find / -perm /a+w -type f          # 任何人有写权限的普通文件(/=任一)
find /home -user alice             # alice 所有的文件
find /home -group developers       # 属于 developers 组的文件
find / -nouser                     # 没有有效所有者的文件(系统清理时有用)

# ─────── 对搜索结果执行操作 ─────────────────────
find . -name "*.log" -exec rm {} \;         # 删除所有 .log 文件({} 替换为文件名)
find . -name "*.py" -exec wc -l {} +        # 统计所有 Python 文件行数(+ 批量执行更高效)
find . -type d -exec chmod 755 {} \;        # 递归设置目录权限
find /tmp -mtime +7 -delete                 # 删除7天前的文件(-delete 是内置动作,比 exec rm 快)

# find + xargs(比 exec 更高效,一次处理多个文件)
find . -name "*.py" | xargs grep "import os"   # 在所有 py 文件中搜索
find . -name "*.py" -print0 | xargs -0 grep "import os"  # 处理含空格的文件名
find . -name "*.txt" | xargs -I {} cp {} /backup/  # -I {} 指定替换字符串

# 组合条件(-a = AND 默认,-o = OR,! = NOT)
find . -type f -name "*.py" -size +10k     # AND(隐式)
find . \( -name "*.py" -o -name "*.rb" \)  # OR(括号需转义)
find . ! -name "*.py"                      # NOT:非 .py 文件
find 的性能注意事项

在根目录 / 运行 find 会遍历整个文件系统(包括 /proc、/sys、网络文件系统等),可能非常慢并产生大量"Permission denied"错误。最佳实践:① 尽量缩小搜索范围;② 使用 2>/dev/null 抑制权限错误;③ 添加 -maxdepth N 限制搜索深度;④ 对常见文件名搜索,locate 命令速度更快。

locate 与 updatedb:快速文件定位

locate 基于预建的文件名数据库(由 updatedb 定期更新)进行毫秒级搜索,比 find 快数百倍,但搜索结果可能略有延迟(新文件在下次 updatedb 运行前不会出现)。

# 安装 locate(Ubuntu/Debian)
sudo apt install mlocate    # 或 plocate(更快的现代版本)

# locate 常用选项
locate nginx.conf                  # 毫秒级找到所有包含 nginx.conf 的路径
locate -i "readme.md"              # 大小写不敏感
locate -c "*.py"                   # 只统计匹配数量(不显示路径)
locate -l 10 "*.log"               # 只显示前 10 个结果
locate -r "\.conf$"                # 使用正则表达式
locate --existing nginx.conf       # 只显示当前实际存在的文件(过滤已删除的)

# updatedb:更新 locate 数据库
sudo updatedb                      # 手动更新(通常由 cron 每天自动执行)
sudo updatedb --prunepaths="/tmp /proc /sys"  # 排除指定目录

# locate vs find 的选择:
# locate:适合按文件名快速查找(毫秒级),但有数据库延迟
# find  :适合按属性过滤(时间/大小/权限),实时搜索,更灵活

高效导航技巧

# ─────── Tab 自动补全(最重要的技巧!)─────────
cd /home/[Tab]               # 补全路径:显示所有 /home 下的目录
cat /etc/ng[Tab]             # 唯一匹配则直接补全,多个匹配则显示列表
cd /usr/lib/p[Tab][Tab]      # 双 Tab 显示所有以 p 开头的选项
git [Tab][Tab]               # 补全 git 子命令(zsh/bash 都支持)

# ─────── 命令历史 ────────────────────────────
history                      # 显示历史命令(默认保留 1000-2000 条)
history 20                   # 显示最近 20 条
!!                           # 执行上一条命令(常用:sudo !! 以 sudo 重跑上条)
!ls                          # 执行最近一条以 ls 开头的命令
!500                         # 执行历史第 500 条命令
Ctrl+R                       # 反向搜索历史(输入关键字,Enter 执行,Ctrl+C 取消)
Ctrl+G                       # 退出历史搜索
fc                           # 在编辑器中编辑并执行上一条命令

# ─────── 路径特殊字符 ────────────────────────
~                            # 当前用户主目录(/home/username 或 /root)
~alice                       # alice 用户的主目录(不需要知道具体路径)
.                            # 当前目录(./script.sh 运行当前目录脚本)
..                           # 父目录

# ─────── pushd/popd:目录栈(高效多目录跳转)──
pushd /etc/nginx             # 进入目录并压入栈(可以用 cd 返回)
pushd /var/log               # 再进入另一目录(栈:/var/log /etc/nginx ~)
pushd /tmp                   # 继续压栈
dirs -v                      # 显示目录栈(带编号)
# 输出:
#  0  /tmp
#  1  /var/log
#  2  /etc/nginx
#  3  ~
popd                         # 弹出栈顶,返回上一目录(/var/log)
pushd +2                     # 跳转到栈中第 2 个目录(轮转方式)

# ─────── zoxide:智能目录跳转(现代工具)──────
# 安装:curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
z projects                   # 跳转到最近使用的包含 "projects" 的目录(模糊匹配)
z ng logs                    # 多个关键词:找最近访问的包含 ng 和 logs 的目录
zi                           # 交互式选择目录(需安装 fzf)

Shell 通配符与大括号展开

# 通配符(Glob):Shell 在执行命令前展开
# * = 匹配任意数量任意字符(不含 /)
ls *.py                      # 所有 .py 文件
ls chapter*.html             # chapter 开头的 html 文件
rm *.tmp                     # 删除所有 .tmp 文件

# ? = 匹配任意单个字符
ls chapter?.html             # chapter1.html, chapter2.html... 不含 chapter10.html
ls file_??                   # file_aa, file_ab 等(恰好两个字符后缀)

# [...] = 字符集合匹配
ls file[123].txt             # file1.txt, file2.txt, file3.txt
ls file[a-z].txt             # file 后接单个小写字母
ls file[!0-9].txt            # ! = NOT:file 后接非数字字符

# 大括号展开 {} = 生成多个变体(不是通配符,不需要文件存在)
echo file{1,2,3}.txt         # file1.txt file2.txt file3.txt
mkdir {jan,feb,mar,apr}      # 批量创建目录
cp file.py{,.bak}            # 备份文件:cp file.py file.py.bak
echo {a..z}                  # 序列:a b c ... z
echo {1..10..2}              # 步进序列:1 3 5 7 9
mkdir /var/log/{nginx,app}/{access,error}  # 嵌套展开:4个目录

# ** 双星号(需要 globstar 开启)
# 匹配任意深度的路径(含子目录)
shopt -s globstar            # 开启 globstar(bash)
ls **/*.py                   # 当前目录及所有子目录下的 .py 文件
本章小结

Linux 文件系统导航的核心要点:① cd - 在两个目录之间快速切换;② ls -lh 是查看目录内容最常用的选项组合;③ 理解绝对路径和相对路径的区别,脚本中优先使用绝对路径;④ find 是功能最强的搜索命令,支持按类型/时间/大小/权限过滤;⑤ locate 更快但有延迟,适合按名称快速查找;⑥ Tab 自动补全是效率核心,Ctrl+R 历史搜索次之;⑦ pushd/popd 适合需要在多个目录间频繁跳转的场景。