Linux 系统管理与运维基础
本文系统归纳 Linux 日常管理与运维核心知识:Linux 哲学与开源协议(GPL/BSD/Apache/MIT)、用户与组管理(useradd/usermod/chage/密码策略)、Shell 类型与配置文件(profile/bashrc/读取顺序)、I/O 重定向与管道(文件描述符/exec/tee)、文件查找(find/locate/xargs)、Vim 编辑器(模式/操作/宏/寄存器/分屏)、系统监控与性能工具(top/free/vmstat/iostat/sar/lsof)、特殊权限要点、重要命令功能速查。大量 Mermaid 图表 与 emoji 标注 辅助理解。每节附注意事项与使用场景。与同系列文档配合使用,内容只增不减。
目录
- [一、Linux 哲学与开源](#一、Linux 哲学与开源)
- 二、用户与组管理
- [三、Shell 类型与配置文件](#三、Shell 类型与配置文件)
- [四、I/O 重定向与管道](#四、I/O 重定向与管道)
- [五、文件查找(find / locate / xargs)](#五、文件查找(find / locate / xargs))
- [六、Vim 编辑器](#六、Vim 编辑器)
- 七、系统监控与性能工具
- 八、特殊权限(SUID/SGID/Sticky)要点
- 九、重要命令功能速查 (新增)
- 十、代表性练习选编
- [10.1 基础练习(原选编)](#10.1 基础练习(原选编))
- [10.2 用户与组管理练习](#10.2 用户与组管理练习)
- [10.3 I/O 重定向与管道练习](#10.3 I/O 重定向与管道练习)
- [10.4 find / locate / xargs 练习](#10.4 find / locate / xargs 练习)
- [10.5 文本处理与管道练习](#10.5 文本处理与管道练习)
- [10.6 权限与属主练习](#10.6 权限与属主练习)
- [10.7 系统监控与综合练习](#10.7 系统监控与综合练习)
- 十一、相关文档索引
一、Linux 哲学与开源
1.1 Linux 重要哲学思想
| 思想 | 说明 |
|------|------|
| 一切皆文件 | 设备、管道、Socket 均以文件形式访问(/dev/null、/dev/sda...) |
| 纯文本配置 | 配置文件、数据多用纯文本,便于查看和脚本处理 |
| 小程序组合 | 每个工具只做一件事并做好,通过管道组合完成复杂任务 |
| 沉默原则 | 命令成功时不输出,只在出错时提示 |
| 避免交互 | 命令优先接受参数和选项,便于脚本自动化 |
1.2 开源协议详解

协议分类
| 类别 | 特点 | 代表协议 |
|---|---|---|
| Copyleft(强传染) | 衍生作品必须同样开源 | GPL、LGPL |
| 宽松(Permissive) | 衍生作品可以闭源商用 | MIT、BSD、Apache |
五大协议详细对比
| 协议 | 闭源发布 | 专利保护 | 传染性 | 核心要求 | 典型项目 |
|---|---|---|---|---|---|
| GPL v2/v3 | 不允许 | v3 有 | 强 | 衍生作品必须 GPL 开源 | Linux 内核、GCC |
| LGPL | 允许动态链接 | - | 弱 | 修改 LGPL 部分须开源 | glibc |
| BSD (2/3-Clause) | 允许 | 无 | 无 | 保留版权声明(3-Clause:不得用原作者推广) | FreeBSD |
| Apache 2.0 | 允许 | 有 | 无 | 保留协议 + 标注修改 + NOTICE 文件 | Kubernetes、Hadoop |
| MIT | 允许 | 无 | 无 | 仅需保留版权声明(最宽松) | React、Vue.js、Node.js |
选择建议
| 场景 | 推荐协议 |
|---|---|
| 个人开源库/前端框架 | MIT |
| 企业商业项目(需专利保护) | Apache 2.0 |
| 确保衍生作品永远开源 | GPL |
| 最大商业灵活性 | BSD |
1.3 内核与发行版
| 概念 | 说明 |
|---|---|
| Linux 内核 | 由 Linus Torvalds 创建,访问 www.kernel.org 获取最新版本 |
| GNU 项目 | 提供编译器 GCC、libc、Bash、coreutils 等用户空间工具 |
| GNU/Linux | 完整操作系统 = Linux 内核 + GNU 工具 + 其他软件 |
| 发行版 | 内核 + 软件包 + 包管理器 + 安装/配置工具;同一内核可被不同发行版使用 |
主要发行版
| 发行版 | 包管理 | 说明 |
|---|---|---|
| RHEL / CentOS / Rocky Linux | yum/dnf (rpm) | 企业级稳定,CentOS 为 RHEL 免费版,CentOS 停维后 Rocky/Alma 接替 |
| Fedora | dnf (rpm) | RHEL 上游社区版,软件较新 |
| SUSE / openSUSE | zypper (rpm) | 企业级,欧洲流行 |
| Debian | apt (deb) | 社区驱动,稳定性高 |
| Ubuntu / Mint | apt (deb) | 基于 Debian,Ubuntu 桌面流行,Mint 更友好 |
| Arch Linux | pacman | 滚动更新,极简,DIY |
| Gentoo | portage(源码编译) | 高度可定制,编译安装 |
| LFS | - | 不是发行版,是指导从源码构建系统的书 |
二、用户与组管理
2.0 用户与组管理概览

2.1 用户类型
| 类型 | UID 范围(典型) | 说明 |
|---|---|---|
| root | 0 | 超级管理员,拥有一切权限 |
| 系统用户 | 1~999 | 服务运行账户,不可登录 |
| 普通用户 | 1000+ | 日常使用账户 |
范围由
/etc/login.defs中的UID_MIN、UID_MAX定义。
2.2 用户管理命令
| 命令 | 说明 | 常用选项 |
|---|---|---|
| useradd | 添加用户 | -u UID、-g 主组、-G 附加组、-s shell、-d 家目录、-m 创建家目录、-M 不创建、-r 系统用户、-c 注释、-e 过期日期 |
| usermod | 修改用户 | -u -g -G(-a 追加而非覆盖附加组)、-s、-d(-m 迁移家目录)、-L 锁定、-U 解锁 |
| userdel | 删除用户 | -r 同时删除家目录和邮箱 |
| passwd | 修改密码 | -n 最短天数、-x 最长天数、-l 锁定、-u 解锁、--stdin 从管道读密码 |
| id | 查看用户信息 | -u 仅 UID、-g 仅 GID、-G 所有组 |
示例:
bash
useradd -c "Fedora Community" -s /bin/tcsh fedora
useradd -r -s /sbin/nologin hbase # 系统用户,不可登录
usermod -u 4004 -g linux -G distro,fedora mandriva
usermod -aG docker myuser # 追加附加组(不覆盖已有组)
passwd -n 2 -x 50 fedora # 最短 2 天、最长 50 天
echo "newpass" | passwd --stdin username # 非交互设密码(RHEL系)
2.2.1 使用场景汇总
| 场景 | 推荐命令组合 | 说明 |
|---|---|---|
| 新员工入职 | useradd -m -s /bin/bash -c "姓名" user → passwd user → usermod -aG 项目组 user |
创建账户、设密码、加入项目组 |
| 员工离职 | usermod -L user 或 usermod -e 2026-01-01 user |
锁定账户或设失效日期 |
| 临时禁用 | usermod -L user;恢复用 usermod -U user |
不删账号,仅禁止登录 |
| 重命名用户 | usermod -l 新名 -d /home/新名 -m 旧名 |
改登录名并迁移家目录 |
| 批量加组 | for u in a b c; do usermod -aG dev $u; done |
脚本中追加附加组 |
| 仅查 UID | id -u username |
脚本中判断用户是否存在 |
2.2.2 练习
- 创建用户
tom,家目录/home/tom,Shell 为/bin/bash,注释为 "Tom Dev"。 - 将用户
tom追加到组docker和wheel(不覆盖原有组)。 - 锁定用户
tom,再解锁;查看/etc/shadow中该行变化。 - 使用
id仅输出tom的 UID 和所有组 GID。
参考答案(点击展开)
bash
# 1
useradd -m -s /bin/bash -c "Tom Dev" tom
# 2
usermod -aG docker tom && usermod -aG wheel tom
# 3
usermod -L tom; grep tom /etc/shadow # 密码前有 !
usermod -U tom; grep tom /etc/shadow # ! 消失
# 4
id -u tom; id -G tom
注意 :
usermod -G会覆盖 所有附加组!要追加 请用usermod -aG。
2.3 组管理命令
| 命令 | 说明 | 使用场景 |
|---|---|---|
| groupadd | 创建组;-g 指定 GID;-r 系统组 |
🏗️ 新建项目团队组 |
| groupmod | -n 改名;-g 改 GID |
🔧 组更名/合并 |
| groupdel | 删除组(不能删除用户的主组) | 🗑️ 项目结束清理 |
| gpasswd | 组管理:-a 加成员、-d 删成员、-A 设管理员 |
👥 团队成员变动 |
| newgrp | 临时切换主组(登录级别,用 exit 退回) |
🔄 切换工作上下文 |
gpasswd 命令详解
gpasswd 用于管理组成员和组管理员,是管理 /etc/group 和 /etc/gshadow 的前端工具:
| 选项 | 说明 | 示例 |
|---|---|---|
-a user |
将用户加入组 | gpasswd -a alice dev |
-d user |
从组中移除用户 | gpasswd -d alice dev |
-A user |
设用户为组管理员 | gpasswd -A bob dev |
-M u1,u2 |
设置组成员列表(覆盖) | gpasswd -M alice,bob dev |
gpasswd group |
设置组密码 | gpasswd developers |
newgrp 命令详解
newgrp 临时切换当前用户的有效主组,之后创建的文件会属于新组:
bash
$ id
uid=1000(alice) gid=1000(alice) groups=1000(alice),5000(developers)
$ newgrp developers # 临时切换主组
$ touch project.txt # 该文件属组变为 developers
$ id -gn # 显示 developers
$ exit # 退出,恢复原主组
使用场景:多团队协作时,临时切换主组,确保创建的文件属于正确的项目组。
bash
groupadd -g 5000 developers
gpasswd -a alice developers # 将 alice 加入组
gpasswd -A bob developers # bob 成为组管理员
newgrp developers # 临时切换主组
组管理使用场景与练习
| 使用场景 | 示例 |
|---|---|
| 新建项目组并指定 GID | groupadd -g 5000 devops |
| 将多个用户加入同一组 | gpasswd -a alice devops;gpasswd -a bob devops |
| 设组管理员(可自行加人) | gpasswd -A alice devops |
| 一次性设置组成员(覆盖) | gpasswd -M alice,bob,carol devops |
| 临时以某组身份创建文件 | newgrp devops → 创建文件 → exit |
练习:
- 创建组
backup(GID 6000),将用户operator加入并设为组管理员。 - 使用
newgrp backup后创建文件/tmp/from_backup.txt,用ls -l确认属组为backup,再exit恢复。
参考答案
bash
groupadd -g 6000 backup
gpasswd -a operator backup
gpasswd -A operator backup
newgrp backup
touch /tmp/from_backup.txt
ls -l /tmp/from_backup.txt # 属组应为 backup
exit
UPG(User Private Group):Red Hat 系为每个用户自动创建同名专用组,用户创建的文件属于该组。
2.4 密码策略:chage 命令详解
chage 是什么?
chage(ch ange age )用于管理用户密码的生命周期策略,控制密码何时过期、何时必须更改。

chage 使用场景与练习
| 使用场景 | 示例 |
|---|---|
| 合规策略:90 天改密、提前 7 天提醒 | chage -m 7 -M 90 -W 7 user |
| 临时账号:年底自动失效 | chage -E 2026-12-31 tempuser |
| 外包/实习:密码过期后 3 天锁定 | chage -I 3 user |
| 审计:查看某用户密码状态 | chage -l user |
| 初始密码分发:首次登录必须改密 | chage -d 0 user |
练习:
- 查看当前用户密码过期信息(
chage -l $USER)。 - 将用户
student设置为 60 天后密码过期,过期前 5 天警告。 - 设置用户
guest在 2026-06-30 账号失效。
参考答案
bash
chage -l $USER
chage -M 60 -W 5 student
chage -E 2026-06-30 guest
2.5 重要配置文件
| 文件 | 说明 | 格式要点 |
|---|---|---|
| /etc/passwd | 用户信息(7 字段) | 用户名:x:UID:GID:注释:家目录:shell |
| /etc/shadow | 密码哈希(9 字段) | 用户名:密码哈希:最后修改:最短:最长:警告:过期:锁定:保留 |
| /etc/group | 组信息(4 字段) | 组名:x:GID:成员列表 |
| /etc/gshadow | 组密码 | 组名:密码:管理员:成员 |
| /etc/login.defs | 用户/密码默认值 | UID_MIN/MAX、PASS_MAX_DAYS 等 |
| /etc/skel/ | 家目录模板 | 新用户家目录从此复制 |
2.6 手动添加用户(原理演示)
以添加用户 hive(UID=5000),主组 hive,附加组 mygroup 为例:
bash
# 1. 添加组
echo "hive:x:5000:" >> /etc/group
# 2. 添加用户
echo "hive:x:5000:5000:Hive:/home/hive:/bin/bash" >> /etc/passwd
# 3. 生成密码哈希
openssl passwd -6 -salt 'random_salt' # SHA-512(推荐)
# 将生成的哈希写入 /etc/shadow
# 4. 创建家目录
cp -r /etc/skel/ /home/hive
chown -R hive:hive /home/hive/
chmod 700 /home/hive/
生产中直接用
useradd,手动操作仅用于理解原理。
2.7 umask 与默认权限
umask 是什么?
umask(u ser file creation mask)定义了新建文件/目录时要**屏蔽(去除)**的权限位。它不是减法,而是按位"遮罩"。
📄 文件最大权限 666
📂 目录最大权限 777
🎭 umask 022
📄 文件实际 644
📂 目录实际 755
| 概念 | 说明 |
|---|---|
| 文件默认最大权限 | 666(无执行位) |
| 目录默认最大权限 | 777 |
| 实际权限 | 最大权限 - umask(按位取反再与) |
| 用户 | 典型 umask | 文件权限 | 目录权限 |
|---|---|---|---|
| root | 022 | 644 | 755 |
| 普通用户 | 002 | 664 | 775 |
bash
umask # 查看(如 0022)
umask -S # 符号显示(u=rwx,g=rx,o=rx)
umask 027 # 设置:文件 640,目录 750
umask 使用场景与练习
| 使用场景 | 典型 umask | 文件权限 | 目录权限 |
|---|---|---|---|
| 默认(root) | 022 | 644 | 755 |
| 严格(仅自己) | 077 | 600 | 700 |
| 组可写(协作) | 002 | 664 | 775 |
| 脚本中临时收紧 | umask 027 |
640 | 750 |
练习:
- 当前 umask 为 022 时,新建文件
touch /tmp/f1、目录mkdir /tmp/d1,分别用ls -l查看权限并写出数字。 - 执行
umask 077后再次创建/tmp/f2和/tmp/d2,观察权限变化。 - 若希望新文件为 640、新目录为 750,应设置 umask 为多少?(答:027)
/etc/bashrc 中的逻辑 :若
UID > 199且用户名 == 主组名(UPG),则 umask=002,否则 022。
2.8 属主属组与 chmod
bash
# chown
chown user:group file # 同时改属主属组
chown -R user:group dir/ # 递归
chown --reference=ref file # 参考某文件
# chmod 符号法
chmod u+x,g-w,o= file # 属主加 x,组去 w,其他清空
chmod a+r file # 所有人加读
# chmod 数字法
chmod 755 script.sh # rwxr-xr-x
chmod 600 secret.txt # rw-------
chown / chmod 使用场景与练习
| 使用场景 | 示例 |
|---|---|
| 部署应用:二进制属主 root、可执行 | chown root:root /usr/local/bin/app;chmod 755 /usr/local/bin/app |
| 配置文件仅 root 可写 | chmod 644 /etc/app.conf 或 chmod 600(敏感时) |
| 目录下所有文件继承属主属组 | chown -R nginx:nginx /var/www/app |
| 参考某文件权限 | chmod --reference=/etc/passwd /tmp/other |
| 脚本可执行 | chmod u+x deploy.sh |
练习:
- 将
/tmp/testfile改为属主读写的 600,再用符号法改为属主可执行(u+x)。 - 将目录
/opt/shared及其下所有文件属主改为nobody,属组改为nogroup。 - 创建文件
secret.txt,要求仅属主可读写,数字法应写多少?(答:600)
参考答案
bash
chmod 600 /tmp/testfile; chmod u+x /tmp/testfile
chown -R nobody:nogroup /opt/shared
touch secret.txt; chmod 600 secret.txt
三、Shell 类型与配置文件
3.0 Shell 配置体系概览

3.1 四种 Shell 组合
| 交互式 | 非交互式 | |
|---|---|---|
| 登录式 | ssh 登录、su - user、tty 终端 |
极少见 |
| 非登录式 | 图形终端、su user、tmux |
执行脚本 |
su - vs su:
| 命令 | 类型 | 读取配置 |
|---|---|---|
su - user 或 su -l user |
登录式 Shell | 完整读取 profile + bashrc |
su user |
非登录式 Shell | 仅读取 bashrc |
3.2 配置文件分类
| 类别 | 全局 | 个人 | 典型用途 |
|---|---|---|---|
| profile 类 | /etc/profile、/etc/profile.d/*.sh | /.bash_profile、/.profile | 环境变量、PATH、启动命令 |
| bashrc 类 | /etc/bashrc | ~/.bashrc | 别名、函数、本地变量、PS1 |
3.3 配置文件读取顺序
登录式 Shell:
text
/etc/profile
→ /etc/profile.d/*.sh
→ ~/.bash_profile
→ ~/.bashrc
→ /etc/bashrc
非登录式 Shell:
text
~/.bashrc → /etc/bashrc → /etc/profile.d/*.sh
作用范围越小的配置越后加载,可覆盖前面的设定。
3.4 常见自定义
| 需求 | 文件 | 示例 |
|---|---|---|
| 设置 PATH | ~/.bash_profile | export PATH="$HOME/bin:$PATH" |
| 定义别名 | ~/.bashrc | alias ll='ls -alF'、alias cls='clear' |
| 登录欢迎语 | ~/.bash_profile | echo "Welcome, $(whoami)! $(date)" |
| 设置 umask | ~/.bash_profile | umask 027 |
| 设置 PS1 | ~/.bashrc | PS1='[\u@\h \W]\$ ' |
| 加载函数库 | ~/.bashrc | source ~/lib/utils.sh |
修改后需
source ~/.bashrc或重新登录才生效。
3.5 注意事项
| 注意点 | 说明 |
|---|---|
| 环境变量放 profile | 非登录式 Shell 不读 profile,但 bashrc 会被读 |
| 别名放 bashrc | 每次打开终端都生效 |
| 脚本中不读配置 | 非交互式不读 bashrc(除非脚本中显式 source) |
| cron 任务无 PATH | 脚本中需定义完整 PATH |
四、I/O 重定向与管道
4.0 I/O 重定向整体概览

4.1 文件描述符

| FD | 名称 | 默认设备 | 说明 |
|---|---|---|---|
| 0 | stdin | 键盘 | 标准输入 |
| 1 | stdout | 显示器 | 标准输出 |
| 2 | stderr | 显示器 | 标准错误输出 |
| 3~9 | - | - | 用户可自定义 |
原理 :I/O 重定向本质是改变文件描述符表中 FD 指向的具体文件(通过
dup2()系统调用)。程序只关心 FD 编号,不关心它指向谁。
4.2 输出重定向
| 符号 | 含义 | 示例 |
|---|---|---|
> |
覆盖 stdout | ls > out.txt |
>> |
追加 stdout | echo "line" >> out.txt |
2> |
覆盖 stderr | cmd 2> err.txt |
2>> |
追加 stderr | cmd 2>> err.txt |
&> |
stdout + stderr 同文件 | cmd &> all.txt |
> file 2>&1 |
同上(POSIX 写法) | cmd > all.txt 2>&1 |
&>> |
追加 stdout + stderr | cmd &>> all.txt |
注意顺序 :
cmd > file 2>&1正确(先重定向 1 到文件,再把 2 复制到 1)。cmd 2>&1 > file错误(2 复制的是原来的 1=屏幕)。
4.3 输入重定向
| 符号 | 含义 | 示例 |
|---|---|---|
< |
从文件读取 | sort < names.txt |
<< |
Here Document | 见下 |
<<< |
Here String | grep "hello" <<< "$var" |
4.4 Here Document
bash
cat << EOF
Hello, $USER
Today is $(date +%F)
EOF
# 写入文件
cat >> /tmp/config.txt << 'EOF'
# 单引号 EOF 防止变量展开
server=localhost
port=8080
EOF
Here Document / Here String 使用场景与练习
| 场景 | 示例 |
|---|---|
| 多行输入给命令 | cat << EOF ... EOF |
| 脚本中生成配置文件(不展开变量) | cat << 'EOF' ... EOF |
| 单行字符串作 stdin | grep "root" <<< "$(cat /etc/passwd)" |
| 从变量读入一行 | read -r line <<< "$var" |
| 生成 SQL 片段 | mysql ... << EOF ... SELECT ...; ... EOF |
练习:
- 用 Here Document 将三行文字(Hello、World、End)写入
/tmp/hello.txt。 - 用 Here String 对字符串 "apple banana apple" 做
grep "apple"并统计出现次数(结合wc -l)。 - 用
<< 'END'形式把字面量$HOME和$(whoami)写入/tmp/literal.txt(不展开变量)。
参考答案
bash
cat > /tmp/hello.txt << EOF
Hello
World
End
EOF
grep "apple" <<< "apple banana apple" | wc -l
cat > /tmp/literal.txt << 'END'
$HOME
$(whoami)
END
4.5 exec 命令详解
exec 是什么?
exec 是 Bash 内建命令,有两大核心功能 :

| 功能 | 语法 | 说明 | 使用场景 |
|---|---|---|---|
| 替换进程 | exec command |
用 command 替换当前 Shell,PID 不变,Shell 消失 | Docker 入口脚本、Wrapper 脚本 |
| 永久重定向 stdout | exec > file |
后续所有 stdout 都写入 file | 日志脚本、批量输出到文件 |
| 永久重定向 stderr | exec 2> file |
后续所有 stderr 都写入 file | 错误日志收集 |
| 永久重定向全部 | exec > file 2>&1 |
stdout + stderr 都写入 file | 脚本全量日志 |
| 打开自定义 FD | exec 3> file |
打开 FD 3 用于写入 | 同时写多个文件 |
| 关闭 FD | exec 3>&- |
关闭 FD 3 | 释放资源 |
| 读取文件 FD | exec 3< file |
打开 FD 3 用于读取 | 逐行读取文件 |
| 读写 FD | exec 3<> file |
打开 FD 3 可读可写 | 双向操作同一文件 |
功能一:exec 替换进程
📦 新命令 🐚 Shell进程 PID=1234 📦 新命令 🐚 Shell进程 PID=1234 Shell 自身被替换 新进程运行 Shell 已不存在 exec 后面的代码不会执行 exec /usr/bin/python3 app.py PID 仍然是 1234
bash
# 例1:用 exec 启动程序,替换当前 Shell
exec /usr/bin/python3 app.py
echo "这行永远不会执行!" # Shell 已被 python3 替换
# 例2:Docker 入口脚本(非常常见!)
#!/bin/bash
# docker-entrypoint.sh
echo "🚀 Initializing..."
# 设置环境变量、生成配置文件等...
export DB_HOST=mysql
envsubst < /etc/myapp/config.tmpl > /etc/myapp/config.conf
# 最后用 exec 启动应用,替换 Shell
# 这样容器的 PID 1 就是应用本身,能正确接收信号
exec java -jar /app/server.jar "$@"
# 例3:Wrapper 脚本(设置环境后启动命令)
#!/bin/bash
export LD_LIBRARY_PATH=/opt/mylib:$LD_LIBRARY_PATH
export JAVA_HOME=/usr/lib/jvm/java-17
exec "$@" # 用传入的命令替换当前 Shell
为什么 Docker 入口脚本要用 exec?
- Docker 发送 SIGTERM 给 PID 1 进程来停止容器
- 不用 exec:PID 1 是 bash,应用收不到信号,无法优雅退出
- 用 exec:PID 1 直接就是应用进程,能正确响应信号
功能二:exec 永久 I/O 重定向

bash
# 例1:脚本日志------所有输出自动写日志文件
#!/bin/bash
LOG="/var/log/myapp/$(date +%F).log"
exec > "$LOG" 2>&1 # 🔒 stdout + stderr → 日志文件
echo "📅 Script started at $(date)"
echo "👤 Running as: $(whoami)"
ls /nonexistent 2>&1 # 错误也写入日志
echo "✅ Script finished"
# 例2:同时保留屏幕输出------先备份再恢复
#!/bin/bash
exec 3>&1 # 📌 FD 3 = 原始 stdout(屏幕)的备份
exec > /tmp/script.log 2>&1 # 🔒 stdout/stderr → 文件
echo "This goes to file only" # 写文件
echo "Also to file" >&2 # stderr 也写文件
echo "I want screen!" >&3 # 💡 通过 FD 3 仍可输出到屏幕!
exec 1>&3 3>&- # 🔓 恢复 stdout → 屏幕,关闭 FD 3
echo "Back to screen" # 屏幕可见
# 例3:自定义 FD 写入多个文件
#!/bin/bash
exec 3> /tmp/success.log # FD 3 → 成功日志
exec 4> /tmp/error.log # FD 4 → 错误日志
for f in /etc/*.conf; do
if [ -r "$f" ]; then
echo "✅ readable: $f" >&3
else
echo "❌ not readable: $f" >&4
fi
done
exec 3>&- # 关闭 FD 3
exec 4>&- # 关闭 FD 4
# 例4:从文件逐行读取(不用 while read 管道)
exec 3< /etc/passwd # FD 3 用于读取
while IFS=: read -r user _ uid _ _ _ shell <&3; do
echo "👤 $user (UID=$uid) → $shell"
done
exec 3<&- # 关闭 FD 3
exec 注意事项
| ⚠️ 注意点 | 说明 |
|---|---|
| exec 替换不可逆 | exec command 后,Shell 消失,后续代码不执行 |
| 永久重定向影响全局 | exec > file 后所有输出都去文件,要恢复需提前备份 FD |
| FD 泄漏 | 打开的自定义 FD 记得用 exec N>&- 关闭 |
| 子进程继承 FD | 子进程会继承父进程打开的 FD,注意安全 |
| 脚本调试困难 | 永久重定向后屏幕无输出,调试时可能困惑 |
4.6 管道与 tee 命令详解
管道(Pipe)是什么?
管道 | 将前一个命令的 stdout 连接到后一个命令的 stdin ,形成数据流水线。

bash
# 管道:前一命令 stdout → 后一命令 stdin
cut -d: -f1 /etc/passwd | sort | uniq
tee 命令是什么?
tee 像一个 T 型水管接头🔧,将数据流同时 分向两个方向:屏幕 和 文件。

| 选项 | 说明 | 示例 |
|---|---|---|
tee file |
覆盖写入文件,同时屏幕显示 | `ls -l |
tee -a file |
追加写入文件 | `echo "new" |
tee f1 f2 |
同时写入多个文件 | `echo "hi" |
sudo tee |
解决 sudo > 无权限问题 |
`echo "line" |
bash
# tee:分流,既输出屏幕又写文件
ls -l | tee /tmp/listing.txt
# tee -a 追加
echo "new line" | tee -a /tmp/log.txt
# 💡 常见技巧:sudo 写系统文件
# ❌ 错误:sudo echo "127.0.0.1 myhost" > /etc/hosts (重定向在 sudo 之外)
# ✅ 正确:
echo "127.0.0.1 myhost" | sudo tee -a /etc/hosts
# 管道只传 stdout,要传 stderr 需:
cmd 2>&1 | tee log.txt # stdout + stderr 都进管道
cmd |& tee log.txt # Bash 4+ 简写
使用场景:
- 长时间运行的命令,想实时看输出 同时记录日志
- 需要用
sudo写入系统配置文件- 将同一份数据同时写入多个文件
tee 更多示例与使用场景
| 场景 | 示例 |
|---|---|
| 管道中保存中间结果并继续处理 | `ls -l |
| 同时写入多个文件 | `echo "backup done" |
| 只保存到文件、不显示在屏幕 | `cmd |
| 捕获命令的 stdout+stderr | `cmd 2>&1 |
| 实时跟踪日志并保存 | `tail -f /var/log/syslog |
| 结合 sudo 写 root 文件 | `echo "options" |
I/O 与 tee 练习
- 将
ls /etc的结果同时保存到/tmp/etclist.txt并在屏幕显示。 - 将
date的输出追加到/tmp/dates.log,且屏幕也要看到。 - 用 tee 实现:执行
find /etc -name "*.conf"的结果既保存到文件又统计行数(wc -l)。 - 不切换 root,向
/etc/hosts追加一行127.0.0.1 mytest(用 tee)。
参考答案
bash
ls /etc | tee /tmp/etclist.txt
date | tee -a /tmp/dates.log
find /etc -name "*.conf" | tee /tmp/confs.txt | wc -l
echo "127.0.0.1 mytest" | sudo tee -a /etc/hosts
4.7 重定向顺序陷阱图解

记忆口诀 :先定向、后复制。
2>&1复制的是此刻 FD 1 指向的目标。
4.8 set -C 防覆盖
bash
set -C # 开启:禁止 > 覆盖已有文件
echo "x" > exist.txt # 报错!
echo "x" >| exist.txt # >| 强制覆盖
set +C # 关闭
使用场景:防止脚本中误覆盖重要文件,特别是日志和配置文件。
五、文件查找(find / locate / xargs)
5.0 文件查找工具对比

5.1 locate vs find
| 对比项 | locate | find |
|---|---|---|
| 数据来源 | 数据库(/var/lib/mlocate) | 实时遍历目录 |
| 更新 | updatedb(cron 每日) |
无需 |
| 匹配 | 模糊匹配路径 | 精确、多条件 |
| 速度 | 极快 | 较慢 |
| 使用场景 | 快速定位已知文件名 | 条件复杂、需精确匹配 |
5.2 find 完整用法
text
find [路径] [条件] [动作]
按名称
| 条件 | 说明 | 示例 |
|---|---|---|
-name |
精确匹配(大小写敏感) | find / -name "*.conf" |
-iname |
忽略大小写 | find / -iname "readme*" |
-regex |
正则匹配完整路径 | find . -regex ".*\.txt$" |
-path |
匹配路径 | find /usr -path "*local*" |
按属主/权限
| 条件 | 说明 |
|---|---|
-user / -group |
属主 / 属组 |
-nouser / -nogroup |
无属主 / 无属组 |
-perm 644 |
精确匹配 644 |
-perm -644 |
包含 644 所有位 |
-perm /644 |
任一位匹配即可 |
按类型/大小/时间
| 条件 | 说明 |
|---|---|
-type f/d/l/b/c/s/p |
文件类型 |
-size +10M |
大于 10MB |
-size -1k |
小于 1KB |
-mtime -7 |
7 天内修改过 |
-mtime +30 |
30 天前修改 |
-mmin -10 |
10 分钟内修改 |
-newer file |
比 file 更新 |
-maxdepth N |
最大搜索深度 |
组合条件
bash
# 与(默认)
find . -name "*.log" -size +1M
# 或
find . -name "*.txt" -o -name "*.md"
# 非
find . -not -user root
# 分组
find /usr -not \( -user root -o -user bin \)
处理动作
| 动作 | 说明 |
|---|---|
-print |
默认,打印路径 |
-ls |
类似 ls -dils 格式 |
-delete |
直接删除 |
-exec CMD {} \; |
对每个结果执行命令 |
-ok CMD {} \; |
同上,需确认 |
bash
find /etc -size +1M -exec ls -lh {} \;
find /tmp -mtime +30 -delete
find . -name "*.bak" -ok rm {} \;
find / \( -nouser -o -nogroup \) -exec chown root:root {} \;
find 使用场景与练习
| 使用场景 | 示例 |
|---|---|
| 清理临时/旧文件 | find /tmp -mtime +7 -type f -delete |
| 找大文件 | find / -type f -size +100M 2>/dev/null |
| 找空文件/空目录 | find . -empty |
| 找 7 天内修改的配置 | find /etc -name "*.conf" -mtime -7 |
| 忽略权限错误 | find / -name "*.log" 2>/dev/null |
| 限制深度 | find /usr -maxdepth 2 -name "*.h" |
| 按 inode 查 | find . -inum 12345 |
练习:
- 在
/etc下查找大于 1MB 的普通文件并列出ls -lh。 - 查找当前目录下 30 天未修改的
.log文件并删除(先去掉-delete用-print确认)。 - 查找
/home下属主为student且扩展名为.sh的文件。 - 查找系统中无属主的文件(
-nouser),仅列出前 10 个。
参考答案
bash
find /etc -type f -size +1M -exec ls -lh {} \;
find . -name "*.log" -mtime +30 -delete # 确认后改用 -delete
find /home -user student -name "*.sh"
find / -nouser 2>/dev/null | head -10
5.3 xargs 命令详解
xargs 是什么?
xargs 从 stdin 读取数据,将其拆分后作为参数传递给后面的命令。它是连接"管道"和"不接受 stdin 的命令"之间的桥梁。

| 选项 | 说明 | 示例 |
|---|---|---|
xargs |
默认将 stdin 作为参数传给 echo | `echo "a b c" |
-n N |
每次最多传 N 个参数 | `... |
-I {} |
用 {} 占位符替换每个参数 |
`... |
-0 |
以 NULL 分隔(配合 -print0) |
`find . -print0 |
-p |
执行前确认 | `... |
-t |
打印将要执行的命令 | `... |
-P N |
并行执行 N 个进程 | `... |
bash
# 比 -exec 更高效:批量传参
find . -name "*.log" | xargs rm -f
find . -name "*.txt" | xargs grep "error"
# 文件名含空格时(⚠️ 必须用 -print0 + -0)
find . -name "*.log" -print0 | xargs -0 rm -f
# 限制每次传入参数数
find . -name "*.jpg" | xargs -n 5 ls -l
# -I {} 占位符:逐个复制到 /backup
find . -name "*.conf" | xargs -I {} cp {} /backup/
# 💡 并行压缩:4 个进程同时 gzip
find . -name "*.log" | xargs -P 4 -n 1 gzip
# 💡 统计多个文件的行数
find . -name "*.py" | xargs wc -l
xargs vs -exec 对比:
| 对比项 |
find -exec {} \;|find | xargs|
|------|--------------------|-------------------|
| 调用方式 | 每个文件调用一次命令 | 批量传参,调用次数少 |
| 性能 | 较慢 | ⚡ 更快 |
| 空格处理 | 自动处理 | 需-print0 + -0|
| 灵活性 | 简单 | 支持-n/-P/-I|
locate 使用场景与练习
| 使用场景 | 示例 |
|---|---|
| 快速找命令路径 | locate -i passwd(-i 忽略大小写) |
| 更新数据库后查找 | sudo updatedb;locate nginx.conf |
| 限制条数 | locate -n 20 "*.conf" |
练习:
- 用 locate 查找系统中名为
passwd的文件(忽略大小写),数一数有多少个。 - 查找名字中包含
grub的路径,只显示前 5 条。
参考答案
bash
locate -i passwd | wc -l
locate -n 5 grub
find + xargs 综合练习
- 将当前目录及子目录下所有
.txt文件复制到/backup(文件名含空格时用-print0/-0)。 - 查找
/var/log下 7 天前的.log文件并用 gzip 压缩(用 xargs,每次传一个参数)。 - 用 xargs 对
find . -name "*.c"的结果执行wc -l,得到每个文件行数及总行数。
参考答案
bash
find . -name "*.txt" -print0 | xargs -0 -I {} cp {} /backup/
find /var/log -name "*.log" -mtime +7 | xargs -n 1 gzip
find . -name "*.c" | xargs wc -l
六、Vim 编辑器
6.0 Vim 模式切换总览

6.1 三种基本模式
| 模式 | 进入方式 | 用途 | 退出 |
|---|---|---|---|
| 普通模式 | 打开文件默认、Esc | 移动、删除、复制 | - |
| 插入模式 | i/a/o/I/A/O/s/c | 输入文本 | Esc |
| 命令行模式 | : |
保存/退出/替换/设置 | Esc 或 Enter |
额外模式:可视模式 (v/V/Ctrl-v)、替换模式(R)。
6.2 打开与关闭
| 操作 | 命令 |
|---|---|
| 打开 | vim file |
| 打开定位第 N 行 | vim +N file |
| 打开定位末行 | vim + file |
| 打开定位匹配行 | vim +/pattern file |
| 保存退出 | :wq 或 :x 或 ZZ |
| 不保存退出 | :q! |
| 强制保存 | :w! |
| 全部退出 | :qa |
6.3 移动(普通模式)
| 范围 | 命令 |
|---|---|
| 字符 | h j k l(左下上右) |
| 单词 | w 词首、b 前词首、e 词尾 |
| 行内 | 0 行首、^ 首非空、$ 行尾 |
| 行间 | NG 第 N 行、G 末行、gg 首行 |
| 翻屏 | Ctrl+f/b 全屏、Ctrl+d/u 半屏 |
| 匹配括号 | % 跳转到配对括号 |
6.4 编辑操作
| 操作 | 命令 | 说明 |
|---|---|---|
| 删除 | dd #dd d$ dw dG |
删行/N 行/到行尾/词/到末行 |
| 复制 | yy #yy y$ yw |
用法同 d |
| 粘贴 | p(后)P(前) |
整行粘贴在下/上方 |
| 修改 | cc cw c$ |
删除并进入插入模式 |
| 删字符 | x(后)X(前) |
|
| 撤销 | u |
多次 u 可连续撤销 |
| 重做 | Ctrl+r |
|
| 重复 | . |
重复上次编辑操作 |
6.5 查找与替换
vim
/pattern " 向下搜索
?pattern " 向上搜索
n / N " 下一个 / 上一个
* " 搜索光标下的单词
" 替换
:s/old/new/ " 当前行第一个
:s/old/new/g " 当前行全部
:%s/old/new/g " 全文替换
:%s/old/new/gc " 全文替换并逐个确认
:3,10s/old/new/g " 第 3~10 行
分隔符可替换:
:%s#/usr/bin#/bin#g(用 # 避免转义 /)。
6.6 可视模式
| 模式 | 快捷键 | 选择方式 |
|---|---|---|
| 字符可视 | v |
按字符选择 |
| 行可视 | V |
整行选择 |
| 块可视 | Ctrl-v |
矩形块选择 |
vim
" 块可视:给多行行首加注释
Ctrl-v → 选择多行 → I → # → Esc
" 块可视:给多行行尾加分号
Ctrl-v → 选择多行 → $ → A → ; → Esc
6.7 寄存器
| 寄存器 | 说明 |
|---|---|
"" |
匿名寄存器(默认) |
"0 |
最近一次复制 |
"1~"9 |
最近 9 次删除 |
"a~"z |
26 个命名寄存器(大写追加) |
"+ / "* |
系统剪贴板 |
"_ |
黑洞寄存器(删除不保存) |
"/ |
最近搜索模式 |
"% |
当前文件名 |
vim
"ayy " 复制到寄存器 a
"ap " 从寄存器 a 粘贴
"Ayy " 追加到寄存器 a
"+yy " 复制到系统剪贴板
"+p " 从系统剪贴板粘贴
:reg " 查看所有寄存器
Ctrl-r a " 插入模式中插入寄存器 a
6.8 宏录制
vim
qa " 开始录制到寄存器 a
... 操作 ...
q " 停止录制
@a " 执行宏 a
@@ " 重复上次宏
99@a " 执行 99 次(遇错自动停止)
6.9 多文件与分屏
vim
" 多文件
vim file1 file2
:next / :prev / :first / :last
:qa " 全部退出
" 分屏
Ctrl-w s " 水平分
Ctrl-w v " 垂直分
Ctrl-w w " 切换窗口
Ctrl-w q " 关闭当前窗口
vim -o f1 f2 " 水平分屏打开
vim -O f1 f2 " 垂直分屏打开
6.10 与 Shell 交互及读写
vim
:r /path/to/file " 读入文件内容
:r !date " 插入命令输出
:20,30w /tmp/snippet.txt " 导出指定行
:! ls -l " 执行 Shell 命令
6.11 常用设置与 vimrc
| 设置 | 命令 |
|---|---|
| 行号 | :set nu / :set nonu |
| 忽略大小写 | :set ic / :set noic |
| 自动缩进 | :set ai / :set noai |
| 搜索高亮 | :set hlsearch / :set nohlsearch |
| 语法高亮 | :syntax on / :syntax off |
| Tab 宽度 | :set tabstop=4 shiftwidth=4 expandtab |
配置文件:全局 /etc/vimrc、个人 ~/.vimrc。
Vim 使用场景与练习
| 场景 | 操作 |
|---|---|
| 打开并跳到第 50 行 | vim +50 file 或 :50 |
| 打开并定位到某字符串 | vim +/pattern file |
| 多文件时下一个/上一个 | :next / :prev |
| 块可视:多行行首加 # | Ctrl-v → 选行 → I → # → Esc |
| 全文替换且逐个确认 | :%s/old/new/gc |
| 只对 10--20 行替换 | :10,20s/old/new/g |
| 读入命令输出到当前缓冲 | :r !date |
| 保存选中行到文件 | 可视选中后 :w! >> /tmp/sel.txt |
练习:
- 在 Vim 中把全文的
foo替换为bar,且每次替换前确认(gc)。 - 用块可视在 5 行行首插入
#(注释)。 - 不退出 Vim,执行
:r !ls -l把当前目录列表插入到光标下。
参考答案
vim
:%s/foo/bar/gc
Ctrl-v → 选 5 行 → I → # → 空格 → Esc
:r !ls -l
七、系统监控与性能工具
7.0 监控工具地图

7.1 监控工具总览
| 工具 | 主要监控对象 | 使用场景 |
|---|---|---|
| top / htop | 进程、CPU、内存 | 实时查看系统负载与进程 |
| free | 内存、Swap | 快速查看内存使用 |
| vmstat | CPU、内存、I/O、Swap | 整体性能概览 |
| iostat | 磁盘 I/O、CPU | 磁盘瓶颈排查 |
| sar | 综合(CPU/内存/网络/磁盘) | 历史性能分析 |
| uptime | 负载 | 快速查看 1/5/15 分钟负载 |
| du / df | 磁盘空间 | 磁盘使用排查 |
| lsof | 打开文件/端口 | 查端口占用/恢复文件 |
7.2 top 详解
输出行解读:
| 行 | 内容 | 关键指标 |
|---|---|---|
| 第 1 行 | 系统时间、运行时长、用户数、负载 | load average > CPU 核数则过载 |
| 第 2 行 | 进程数(running/sleeping/zombie) | zombie > 0 需关注 |
| 第 3 行 | CPU 状态 | us+sy > 80% 则 CPU 忙;wa > 10% 则 I/O 等待多 |
| 第 4 行 | 内存 | buff/cache 可回收,不等于"被占" |
| 第 5 行 | Swap | used 持续增长说明内存不足 |
交互命令:
| 按键 | 作用 |
|---|---|
1 |
显示各逻辑 CPU |
P |
按 CPU 排序 |
M |
按内存排序 |
T |
按时间排序 |
k |
杀进程 |
c |
显示完整命令行 |
q |
退出 |
top / free / vmstat / iostat 使用场景与练习
| 工具 | 使用场景 | 示例 |
|---|---|---|
| top | 实时看 CPU/内存占用最高的进程 | 进入 top 后按 P(CPU)或 M(内存) |
| top | 批处理模式(脚本中) | `top -b -n 1 |
| free | 快速看内存是否紧张 | free -h,关注 available |
| vmstat | 持续观察整体负载 | vmstat 2(每 2 秒刷新) |
| vmstat | 采样若干次 | vmstat 1 5(共 5 次) |
| iostat | 看磁盘 I/O 瓶颈 | iostat -x 2 3(扩展输出,每 2 秒,3 次) |
| iostat | 仅 CPU | iostat -c 1 3 |
练习:
- 用一条命令输出当前负载(1/5/15 分钟)和内存概要(可用内存)。
- 用
vmstat 1 3观察输出,说明r、b、wa分别代表什么。 - 用
free -h写出「实际可用内存」对应哪一列。 - 用
top -b -n 1取前 15 行保存到/tmp/top_snapshot.txt。
参考答案
bash
uptime; free -h | grep Mem
# r=运行队列 b=阻塞进程 wa=I/O 等待
# available 列
top -b -n 1 | head -15 > /tmp/top_snapshot.txt
7.3 free 详解
bash
free -h # 人类可读格式
| 字段 | 含义 |
|---|---|
| total | 物理内存总量 |
| used | 已使用(含 buff/cache) |
| free | 完全空闲 |
| buff/cache | 缓冲/缓存(可回收) |
| available | 实际可用(free + 可回收 buff/cache) |
看 available 而非 free。available 低于总内存 20% 时应关注。
7.4 vmstat 详解
bash
vmstat 2 5 # 每 2 秒采样,共 5 次
| 字段 | 含义 | 告警阈值 |
|---|---|---|
| r | 运行队列长度 | > CPU 核数 → CPU 忙 |
| b | 阻塞进程数 | > 0 持续 → I/O 瓶颈 |
| swpd | 已用虚拟内存 | > 0 → 物理内存不足 |
| si / so | 每秒 swap 换入/出 | > 0 持续 → 内存紧张 |
| bi / bo | 每秒块读/写 | 突增 → I/O 操作频繁 |
| cs | 每秒上下文切换 | 过高 → 线程/进程过多 |
| us | 用户态 CPU | |
| sy | 内核态 CPU | us+sy > 80% → CPU 忙 |
| id | 空闲 CPU | < 20% → 过载 |
| wa | I/O 等待 | > 30% → I/O 瓶颈 |
7.5 iostat 详解
bash
iostat -d -x -k 2 3 # 每 2 秒,共 3 次,详细磁盘信息
| 指标 | 含义 | 告警 |
|---|---|---|
| r/s, w/s | 每秒读/写请求数 | |
| rkB/s, wkB/s | 每秒读/写数据量 | |
| await | 平均 I/O 等待(ms) | > svctm 很多则队列长 |
| svctm | 平均服务时间(ms) | |
| %util | I/O 使用率 | > 70% → 瓶颈 |
7.6 du / df
bash
df -Th # 文件系统类型 + 可读
df -ih # inode 使用情况
du -sh /var/log # 目录总大小
du -h --max-depth=1 /var # 一级子目录大小
du -sh /var/log/* | sort -rh # 排序找大目录
du / df 使用场景与练习
| 场景 | 示例 |
|---|---|
| 看根分区与各挂载点使用率 | df -h |
| 看 inode 是否耗尽 | df -ih |
| 找目录下最大的子目录 | `du -sh /var/* |
| 只看一级子目录大小 | du -h --max-depth=1 /usr |
| 排除某类型 | du -sh /var/log/* 或结合 find |
练习:
- 统计
/var下占用空间最大的前 3 个子目录。 - 查看根分区
/的 inode 使用率(df -ih /)。 - 用 du 计算当前用户家目录总大小(人类可读)。
参考答案
bash
du -sh /var/* 2>/dev/null | sort -rh | head -3
df -ih /
du -sh $HOME
7.7 lsof 命令详解
lsof 是什么?
lsof = L is t O pen F iles,列出系统中所有被进程打开的文件 。由于 Linux "一切皆文件",所以 lsof 能查看:普通文件、目录、网络连接(Socket)、管道、设备等。

| 选项 | 说明 | 使用场景 |
|---|---|---|
lsof -i :PORT |
查看端口占用 | 🌐 排查端口冲突:"谁占了 80 端口?" |
lsof -i tcp |
查看所有 TCP 连接 | 🌐 网络排查 |
lsof -u USER |
某用户打开的文件 | 👤 审计用户行为 |
lsof +D DIR |
某目录下被打开的文件 | 📂 排查 umount "device busy" |
lsof -p PID |
某进程打开的文件 | ⚙️ 分析进程资源 |
lsof FILE |
谁在使用某文件 | 📄 排查文件锁 |
lsof -c NAME |
按命令名过滤 | ⚙️ 查看 nginx/java 打开了什么 |
bash
lsof -i :80 # 查看 80 端口被谁占用
lsof -i :8080 -i :3306 # 同时查多个端口
lsof -u username # 某用户打开的文件
lsof +D /var/log # 某目录下被打开的文件
lsof -p PID # 某进程打开的文件
lsof /path/to/file # 谁在使用某文件
lsof -c nginx # nginx 进程打开的所有文件
# 💡 查看进程打开的网络连接
lsof -i -a -p $(pgrep nginx)
# 💡 查找删除但未释放的大文件(磁盘满排查利器!)
lsof +L1 # 列出 link count < 1 的文件(已删除但仍被占用)
误删文件恢复 :若文件被删但仍有进程持有 FD,可从
/proc/PID/fd/N恢复:
bash# 1. 找到被删文件的进程和 FD lsof | grep deleted # 2. 从 /proc 恢复 cp /proc/PID/fd/N /tmp/recovered_file场景:误删了正在被 nginx 使用的日志文件,但 nginx 仍持有文件句柄,可以用此方法恢复。
lsof 使用场景与练习
| 场景 | 示例 |
|---|---|
| 端口被占用排查 | lsof -i :80、lsof -i :8080 |
| 某用户打开的文件 | lsof -u nginx |
| 某进程打开的文件与网络 | lsof -p $(pgrep nginx) |
| 谁在用某目录(umount 失败时) | lsof +D /mnt/data |
| 查找已删除仍被占用的文件(磁盘满) | lsof +L1 或 `lsof |
练习:
- 查看 22 端口被哪个进程占用。
- 列出当前用户打开的所有文件数(
lsof -u $USER \| wc -l)。 - 若某文件被删但进程仍占用,写出从
/proc恢复的步骤。
参考答案
bash
lsof -i :22
lsof -u $USER | wc -l
# 1) lsof | grep deleted 找到 PID 和 FD
# 2) cp /proc/PID/fd/FD /path/recovered
7.8 性能排查思路(USE 法则)
uptime 负载 > 核数
正常
free 的 available 低
正常
iostat %util > 70%
正常
sar -n DEV 检查
正常
🚨 系统变慢了!
🔥 CPU 负载高?
top 按 P 排序
找高 CPU 进程
🧠 内存不足?
top 按 M 排序
找内存大户
检查 swap 使用
💾 磁盘 I/O 慢?
iotop 找 I/O 大户
检查 await
🌐 网络问题?
ss 查连接数
检查带宽/延迟
⚙️ 应用层问题
查看日志/strace
USE 法则(Utilization, Saturation, Errors):对每个资源检查利用率、饱和度、错误数。
| 步骤 | 方法 | 工具 |
|---|---|---|
| 1. 🔥 CPU | 查负载、us/sy/wa | uptime、top、vmstat |
| 2. 🧠 内存 | 查 available、swap | free -h、vmstat |
| 3. 💾 磁盘 I/O | 查 %util、await | iostat、iotop |
| 4. 🌐 网络 | 查带宽、连接数 | sar -n DEV、ss |
| 5. ⚙️ 进程 | 定位消耗资源的进程 | top、pidstat、lsof |
八、特殊权限(SUID/SGID/Sticky)要点

| 权限 | 数字 | 对文件效果 | 对目录效果 | 设置 |
|---|---|---|---|---|
| SUID | 4 | 进程属主=文件属主(如 /usr/bin/passwd) |
- | chmod u+s FILE |
| SGID | 2 | 进程属组=文件属组 | 新建文件继承目录属组 | chmod g+s DIR |
| Sticky | 1 | - | 仅属主和 root 可删除文件(如 /tmp) |
chmod o+t DIR |
bash
# 查找系统中所有 SUID 程序(安全审计)
find / -perm -4000 -type f -ls 2>/dev/null
# 团队共享目录(SGID + Sticky)
mkdir /shared
chgrp team /shared
chmod 3775 /shared # SGID(2) + Sticky(1) = 3
- 有 x 时显示 s/t,无 x 时显示 S/T(后者无实际执行意义)。
- 详见《Linux文件系统与FHS详解》中"文件权限与特殊权限"章节。
九、重要命令功能速查
对本文涉及的重要命令进行功能解释,快速了解"这个命令是干什么的"。
9.1 进程与 Shell 相关
| 命令 | 全称/来源 | 🎯 一句话解释 | 使用场景 |
|---|---|---|---|
| exec | execute | ⚡ 替换当前进程或永久重定向 I/O | Docker 入口、日志重定向、FD 管理 |
source / . |
- | 📥 在当前 Shell 中执行脚本(不新建子进程) | 加载环境变量、函数库 |
| export | - | 📤 将变量导出为环境变量,子进程可见 | 设置 PATH、JAVA_HOME |
| eval | evaluate | 🔄 二次解析并执行字符串为命令 | 动态构建命令 |
| set | - | ⚙️ 设置 Shell 选项(-e/-u/-x/-o pipefail) | 脚本加固 |
| trap | - | 🪤 捕获信号并执行指定动作 | 清理临时文件、优雅退出 |
| nohup | no hang up | 🔌 让命令忽略 SIGHUP,终端关闭仍运行 | 后台长时间任务 |
| disown | - | 🏃 将后台任务从 Shell 作业表中移除 | 防止退出时杀进程 |
| bg / fg | background / foreground | 🔀 将作业切到后台/前台运行 | 作业控制 |

source vs exec vs bash 对比
| 方式 | 命令 | 新进程? | 环境继承 | Shell 继续? |
|---|---|---|---|---|
| source | source script.sh 或 . script.sh |
❌ 不创建 | 变量影响当前 Shell | ✅ 继续 |
| bash | bash script.sh |
✅ 创建子进程 | 子进程变量不影响父进程 | ✅ 继续 |
| exec | exec script.sh |
❌ 替换当前进程 | Shell 消失 | ❌ 不返回 |
bash
# source:加载 .bashrc 到当前 Shell
source ~/.bashrc # 修改立即生效
# bash:子进程执行脚本
bash test.sh # 脚本中定义的变量不影响当前 Shell
# exec:替换当前 Shell
exec bash test.sh # 当前 Shell 被替换,不会返回
9.1.1 更多示例与练习(进程与 Shell 命令)
| 命令 | 示例 | 使用场景 |
|---|---|---|
| source | source /etc/profile.d/java.sh |
加载 Java 环境到当前 Shell |
| export | export DEBUG=1 |
脚本中传递调试开关给子进程 |
| nohup | nohup ./longrun.sh & |
后台跑长时间任务,关闭终端不退出 |
| disown | jobs; disown %1 |
把已启动的后台任务从作业表移除 |
| trap | trap 'rm -f /tmp/$$.tmp' EXIT |
脚本退出时删除临时文件 |
练习 :用 nohup 在后台运行 sleep 300,用 jobs 和 ps 确认;再用 disown 将该项目从 jobs 中移除。
9.2 文件操作相关
| 命令 | 全称/来源 | 🎯 一句话解释 | 使用场景 |
|---|---|---|---|
| tee | T 型管道 | 🔧 将 stdin 同时输出到屏幕和文件 | 记录日志同时实时查看 |
| xargs | ex tended arg uments | 🔗 将 stdin 转为命令行参数 | 配合 find 批量操作 |
| install | - | 📦 复制文件并设置权限/属主(一步到位) | 部署二进制、创建目录 |
| mktemp | make temporary | 🗑️ 安全创建临时文件/目录 | 脚本中临时文件 |
| stat | status | 📊 显示文件的详细元数据(inode/权限/时间) | 查看文件详细信息 |
| file | - | 🔍 识别文件真实类型(不看扩展名) | 判断未知文件格式 |
| ln | link | 🔗 创建硬链接或符号链接 | 共享文件、快捷方式 |
bash
# install:复制+设权限(部署常用)
install -m 755 myapp /usr/local/bin/ # 复制并设 755
install -d -m 700 /opt/myapp/config # 创建目录并设权限
install -o root -g root -m 644 config.conf /etc/myapp/
# mktemp:安全创建临时文件
TMPFILE=$(mktemp) # /tmp/tmp.XXXXXXXXXX
TMPDIR=$(mktemp -d) # 创建临时目录
echo "data" > "$TMPFILE"
rm -f "$TMPFILE" # 用完记得清理
# stat:查看文件详细信息
stat /etc/passwd
# 输出:大小、块数、inode、权限、时间等
# file:识别文件类型
file /bin/ls # ELF 64-bit LSB executable
file /etc/passwd # ASCII text
file image.png # PNG image data
9.2.1 更多示例与练习(文件操作命令)
| 命令 | 示例 | 使用场景 |
|---|---|---|
| tee | `dmesg | tee -a /tmp/dmesg.log |
| xargs | `printf '%s\n' a b c | xargs -I {} echo item: {}` |
| install | install -m 755 -o root script.sh /usr/local/bin/ |
部署脚本并设属主与权限 |
| mktemp | T=$(mktemp -u); echo $T |
只生成临时路径不创建文件(-u) |
| stat | stat -c '%i %n' file |
只输出 inode 和文件名 |
| file | file -b /bin/ls |
简短类型描述(-b 无文件名) |
练习 :用 install -d 创建目录 /opt/myapp/{bin,conf,log}(需 shell 大括号展开),并用 install -m 644 将当前目录下某配置文件复制到 /opt/myapp/conf/。
9.3 文本处理相关
| 命令 | 全称/来源 | 🎯 一句话解释 | 使用场景 |
|---|---|---|---|
| grep | g lobal r egular e xpression print | 🔍 按正则过滤文本行 | 日志搜索、数据过滤 |
| sed | s tream editor | ✂️ 流式编辑器,按行处理文本 | 批量替换、删除行 |
| awk | Aho, Weinberger, Kernighan | 📊 按列处理文本,内建编程语言 | 数据统计、报表生成 |
| cut | - | ✂️ 按分隔符/字符位置截取列 | 提取特定字段 |
| sort | - | 🔢 排序文本行 | 数据排列 |
| uniq | unique | 🔄 去除/统计相邻重复行 | 去重(需先 sort) |
| tr | translate | 🔤 转换/删除字符 | 大小写转换、字符替换 |
| wc | w ord count | 📏 统计行数/词数/字符数 | 快速统计 |
| bc | b asic calculator | 🧮 命令行计算器(支持浮点) | Shell 中做浮点运算 |
bash
# bc:Shell 中唯一方便做浮点运算的工具
echo "scale=2; 10/3" | bc # 3.33
echo "3.14 * 2.5" | bc # 7.85
echo "sqrt(144)" | bc # 12
# tr:字符级转换
echo "hello" | tr 'a-z' 'A-Z' # HELLO
echo "hello world" | tr -s ' ' # 压缩连续空格
echo $PATH | tr ':' '\n' # PATH 每行显示
# cut + sort + uniq 组合
cut -d: -f7 /etc/passwd | sort | uniq -c | sort -rn
# 统计系统中每种 shell 被多少用户使用
9.3.1 更多示例与练习(文本处理命令)
| 命令 | 示例 | 使用场景 |
|---|---|---|
| grep | grep -r "ERROR" /var/log --include="*.log" |
递归在日志中搜错误 |
| sed | sed -i.bak 's/old/new/g' file |
原地替换并备份为 file.bak |
| awk | awk -F: '$3>=1000 {print $1}' /etc/passwd |
打印 UID≥1000 的用户名 |
| cut | cut -d: -f1,3 /etc/passwd |
取用户名和 UID 两列 |
| sort | sort -t: -k3 -n /etc/passwd |
按第 3 列数字排序 |
| uniq | `sort file | uniq -d` |
| tr | tr -d '\r' < win.txt > unix.txt |
去掉 Windows 回车符 |
| wc | wc -l < file |
仅输出行数(无文件名) |
| bc | `echo "2^10" | bc` |
练习 :用 awk 从 /etc/passwd 中输出「用户名:UID」且仅 UID 小于 100 的系统用户;用 grep -c 统计包含 nologin 的行数。
9.4 系统管理相关
| 命令 | 全称/来源 | 🎯 一句话解释 | 使用场景 |
|---|---|---|---|
| chage | ch ange age | 📅 管理密码生命周期策略 | 密码过期、强制修改 |
| chown | ch ange owner | 👤 修改文件属主和属组 | 权限管理 |
| chmod | ch ange mode | 🔐 修改文件权限 | 设置 rwx |
| umask | u ser file creation mask | 🎭 设置新建文件的默认权限掩码 | 控制默认权限 |
| lsof | l is t o pen files | 🔍 列出被进程打开的文件 | 端口排查、恢复删除文件 |
| useradd | - | 👤 添加系统用户 | 创建用户账户 |
| usermod | - | 🔧 修改用户属性 | 改组、改 Shell、锁定 |
| passwd | password | 🔑 修改用户密码 | 设置/重置密码 |
| su | s witch user | 🔄 切换用户身份 | 切 root、测试权限 |
| sudo | su peruser do | 🛡️ 以其他用户身份执行命令 | 临时提权 |
9.4.1 更多示例与练习(系统管理命令)
| 命令 | 示例 | 使用场景 |
|---|---|---|
| chage | chage -l root |
审计 root 密码策略 |
| chown | chown -R apache:apache /var/www |
部署 Web 目录属主 |
| chmod | chmod g+w file |
给组增加写权限 |
| umask | umask 077; touch f; ls -l f |
验证新文件为 600 |
| lsof | `lsof -i -P -n | grep LISTEN` |
| useradd | useradd -m -s /bin/bash -c "Dev" u1 |
创建带家目录的普通用户 |
| usermod | usermod -L u1 |
锁定账户 |
| passwd | passwd -l u1 |
锁定(同 usermod -L) |
| su | su - appuser -c "whoami" |
以 appuser 执行单条命令 |
| sudo | sudo -u nobody id |
以 nobody 查看 id |
练习 :创建用户 deploy,家目录 /home/deploy,加入组 docker;将其 shell 改为 /bin/bash;用 chage -d 0 deploy 强制首次登录改密。
十、代表性练习选编
10.1 基础练习(原选编)
- 取出系统中所有用户的 shell,每种只显示一次并排序:
bash
cut -d: -f7 /etc/passwd | sort -u
- 取 /etc/passwd 倒数第 9 个用户的用户名和 shell,保存并显示:
bash
tail -9 /etc/passwd | head -1 | cut -d: -f1,7 | tee /tmp/users
- find 练习:查找 /etc 下最近一周修改且不属于 root 和 student 的文件:
bash
find /etc -mtime -7 -not -user root -not -user student
- vim 替换练习:全文替换 URL:
vim
:%s@ftp://instructor\.example\.com/pub@http://172.16.0.1/yum@g
- 统计 /var 下各一级子目录大小并排序:
bash
du -sh /var/* 2>/dev/null | sort -rh | head -10
- 查找系统中无属主的文件并修改:
bash
find / \( -nouser -o -nogroup \) -exec chown root:root {} \;
- 强制用户下次登录修改密码:
bash
chage -d 0 username
10.2 用户与组管理练习
- 创建系统用户
redis,不允许登录(shell 为/sbin/nologin),不创建家目录。 - 将用户
alice的主组改为developers,并追加到组docker(不覆盖原有组)。 - 查看用户
bob的 UID、主组 GID、所有组 GID(一行一个命令)。 - 设置用户
temp的账号在 2026-12-31 过期;并设置密码 90 天过期、提前 7 天警告。 - 用
id判断用户nobody是否存在,在脚本中可用if id "nobody" &>/dev/null; then ...。
参考答案
bash
useradd -r -s /sbin/nologin -M redis
usermod -g developers -aG docker alice
id -u bob; id -g bob; id -G bob
chage -E 2026-12-31 temp; chage -M 90 -W 7 temp
id nobody &>/dev/null && echo "exists" || echo "not exists"
10.3 I/O 重定向与管道练习
- 将
ls /usr/bin的标准输出 保存到/tmp/out.txt,标准错误 保存到/tmp/err.txt。 - 将命令
cmd的 stdout 和 stderr 都追加到/tmp/all.log(用>>和2>&1)。 - 用 Here Document 生成文件
/tmp/motd.txt,内容为两行:Welcome和$(date)(要求 date 被展开)。 - 用管道和
tee:把df -h的结果保存到/tmp/df.txt,同时在屏幕上显示。 - 实现:
echo "a:b:c"通过管道传给cut,按冒号分隔取第 2 列(得到b)。
参考答案
bash
ls /usr/bin > /tmp/out.txt 2> /tmp/err.txt
cmd >> /tmp/all.log 2>&1
cat > /tmp/motd.txt << EOF
Welcome
$(date)
EOF
df -h | tee /tmp/df.txt
echo "a:b:c" | cut -d: -f2
10.4 find / locate / xargs 练习
- 查找
/etc下所有以.conf结尾的普通文件,忽略权限错误,只输出路径。 - 查找当前目录下 7 天内修改过的、大于 1MB 的文件。
- 用 find 和 xargs 将
/tmp下所有.bak文件删除(考虑文件名含空格:-print0与-0)。 - 用 find 查找属主为 root 且权限为 755 的可执行文件(
-perm -755),在/usr/bin下找。 - 用 locate 查找包含
cron的路径,限制输出 10 条。
参考答案
bash
find /etc -type f -name "*.conf" 2>/dev/null
find . -mtime -7 -size +1M -type f
find /tmp -name "*.bak" -print0 | xargs -0 rm -f
find /usr/bin -user root -perm -755 -type f
locate cron | head -10
10.5 文本处理与管道练习
- 统计
/etc/passwd的行数;统计其中包含/bin/bash的行数。 - 显示
/etc/passwd的第 3 到第 7 行(用sed -n '3,7p'或head -7 | tail -5)。 - 对
/etc/passwd按冒号取第 1 列,排序后去重,再统计每种用户名的数量(sort | uniq -c)。 - 将字符串 "Hello World" 中的小写字母转为大写(用
tr 'a-z' 'A-Z')。 - 用
grep在/etc/passwd中匹配 UID 为三位数的行(例如grep ':[0-9]\{3\}:'或:x:[0-9]\{3\}:)。
参考答案
bash
wc -l /etc/passwd; grep -c /bin/bash /etc/passwd
sed -n '3,7p' /etc/passwd
cut -d: -f1 /etc/passwd | sort | uniq -c
echo "Hello World" | tr 'a-z' 'A-Z'
grep -E ':[0-9]{3}:' /etc/passwd
10.6 权限与属主练习
- 将文件
script.sh设为:属主可读可写可执行,属组可读可执行,其他人无权限(数字法)。 - 将目录
/srv/shared及其下所有内容属主改为www-data,属组改为www-data。 - 当前 umask 为 022 时,新建目录的权限是多少?新建普通文件的权限是多少?(答:755、644)
- 查找系统中带 SUID 的可执行文件(
find / -perm -4000 -type f 2>/dev/null),数一数有多少个。 - 创建目录
/tmp/team,要求:属组可写、且新建文件继承该组(SGID)、仅属主和 root 可删文件(Sticky)。写出 chmod 数字(如 3770 或 2775 等)。
参考答案
bash
chmod 750 script.sh
chown -R www-data:www-data /srv/shared
# 755 目录,644 文件
find / -perm -4000 -type f 2>/dev/null | wc -l
mkdir /tmp/team; chgrp somegroup /tmp/team; chmod 3770 /tmp/team # 或 2775 等
10.7 系统监控与综合练习
- 用一条命令显示:当前时间、运行时长、负载,以及内存总量和可用内存(
uptime+free -h一行)。 - 将
vmstat 1 5的输出保存到/tmp/vmstat.log。 - 查看 80 端口被哪个进程占用;查看 root 用户当前打开的文件数。
- 统计
/var下各一级子目录占用的磁盘空间,按从大到小排序并取前 5。 - 综合题:查找
/etc下最近 24 小时内修改过的、大于 10KB 的配置文件(.conf),将路径列表保存到/tmp/recent_confs.txt。
参考答案
bash
uptime; free -h | grep -E "Mem|total|available"
vmstat 1 5 > /tmp/vmstat.log
lsof -i :80; lsof -u root | wc -l
du -sh /var/* 2>/dev/null | sort -rh | head -5
find /etc -name "*.conf" -mtime 0 -size +10k -type f 2>/dev/null > /tmp/recent_confs.txt