Linux_reference

Linux 个人参考手册12

默认环境为 Debian/Ubuntu、zsh 与 systemd;涉及 zsh 与 Bash 差异的地方同时给出 Bash 对照。未特别标注的外部 Linux 命令通常可在两者中直接使用。

约定:FILEDIRPIDUSERSERVICEIMAGE 等大写词是待替换占位符;# 开头的命令通常需要 root 权限;示例中的 $# 提示符不要输入。

快速目录

需求 入口
先救急 一页应急速查 · 高频故障排查 · 危险命令检查
文件与文本 路径与文件系统 · 文件操作 · 搜索与空间 · 文本处理 · 正则
系统管理 权限与用户 · 软件包 · 进程与会话 · systemd · 定时任务
联网与开发 网络与 SSH · zsh 与 Bash · C/C++ 与 Python · Docker
自己扩展 文档与自救 · 个人环境备忘

使用原则

  • 不确定命令效果时,先查 tldr COMMANDCOMMAND --helpman COMMAND
  • 修改配置前先备份:cp -a FILE FILE.bak.$(date +%F-%H%M%S)
  • 删除、覆盖、递归、通配符和提权命令执行前,先用只读命令确认目标。
  • 路径和变量几乎总应加双引号:cp -- "$src" "$dst"
  • 文本处理先输出到终端确认,再使用 -i、重定向或 tee 写回。
  • 结束进程先用 SIGTERM,确认无效后再考虑 SIGKILL
  • 长期服务用 systemd;需要保留交互现场用 tmux;临时后台任务才用 nohup
  • 从网络取得的脚本先下载、阅读、验证,再执行;避免直接 curl URL | sh

Shell 约定

  • shell 代码块:zsh 与 Bash 都能使用的命令,或实际由 findgrepsystemctl 等外部程序解析的语法。
  • zsh 代码块:本手册默认写法;脚本使用 #!/usr/bin/env zsh
  • bash 代码块:仅用于给出有差异的 Bash 对照;脚本使用 #!/usr/bin/env bash
  • 交互配置默认写入 ~/.zshrc;Bash 对应 ~/.bashrc。不要把一种 Shell 的专属配置直接复制到另一种配置文件。

命令风险标记

标记 含义 典型命令
🟢 只读,通常可放心执行 lspsdfgrepjournalctl
🟡 会修改用户数据或运行状态 mvchmodsystemctl restartdocker stop
🔴 可能不可恢复或影响全系统 rm -rfmkfsddkill -9、递归 chown

一页应急速查

系统与身份

1
2
3
4
5
6
7
whoami                         # 当前用户名
id # UID、GID、所属组
hostnamectl # 主机名、系统与内核摘要
cat /etc/os-release # 发行版信息
uname -a # 内核与架构
date; uptime # 时间、运行时长、负载
env | sort # 环境变量

资源与故障入口

1
2
3
4
5
6
7
8
9
10
free -h                        # 内存
df -hT # 文件系统容量与类型
df -i # inode 是否耗尽
du -xh --max-depth=1 DIR | sort -h
ps aux --sort=-%cpu | head # CPU 占用靠前的进程
ps aux --sort=-%mem | head # 内存占用靠前的进程
ss -lntup # 监听端口及进程
systemctl --failed # 失败的 systemd 单元
journalctl -p warning -b # 本次启动以来的警告和错误
dmesg -T | tail -n 100 # 近期内核消息,可能需要 sudo

高频操作

1
2
3
4
5
6
7
8
9
10
11
pwd                            # 当前路径
ls -lah # 详细列出全部项目
cd - # 回到上一个目录
mkdir -p DIR/SUBDIR # 递归创建目录
cp -a SOURCE DEST # 尽量保留属性地复制
mv SOURCE DEST # 移动或重命名
rm -i FILE # 交互确认后删除文件
less FILE # 分页查看大文件
tail -F FILE # 持续跟踪日志,日志轮转后继续
find DIR -type f -name '*.log'
grep -RIn --exclude-dir=.git 'TEXT' DIR

服务与日志

1
2
3
4
5
systemctl status SERVICE --no-pager -l
sudo systemctl restart SERVICE
sudo systemctl enable --now SERVICE
journalctl -u SERVICE -b --no-pager
journalctl -u SERVICE -f

网络

1
2
3
4
5
6
7
ip -br addr                    # 地址摘要
ip route # 路由与默认网关
ping -c 4 HOST # 基本连通性
curl -I URL # HTTP 响应头
curl -v URL # HTTP/TLS 详细过程
ss -lntup # 本机监听端口
ssh -v USER@HOST # SSH 详细诊断

路径、导航与文件系统

路径规则

写法 含义
/ 根目录;所有路径的起点
~$HOME 当前用户家目录
... 当前目录、父目录
/path/to/x 绝对路径
path/to/x 相对当前目录的路径
.name 隐藏文件或目录
1
2
3
4
5
6
7
pwd                            # 显示当前绝对路径
cd DIR # 进入目录
cd # 回家目录
cd - # 在最近两个目录间切换
realpath FILE # 转为规范绝对路径
basename /a/b/file.txt # file.txt
dirname /a/b/file.txt # /a/b

根目录下常见位置

路径 用途
/etc 系统及服务配置
/home/USER 普通用户数据
/root root 家目录
/var/log 日志;systemd 日志主要由 journal 管理
/var/lib 服务持久状态和数据库
/run 本次启动期的 PID、socket 等运行数据
/tmp 临时数据,可能在重启或定期清理时消失
/usr/bin/usr/sbin 大多数用户命令和管理命令
/usr/local 管理员手工安装且不归包管理器管理的软件
/opt 独立的附加软件
/dev 设备文件,如/dev/null/dev/urandom
/proc/sys 内核和进程暴露的虚拟文件系统
/mnt/media 临时挂载点、可移动介质挂载点

文件类型与元数据

1
2
3
4
5
file FILE                      # 按内容识别类型,不只看后缀
stat FILE # 权限、所有者、inode、时间戳
readlink LINK # 查看一层软链接目标
readlink -f LINK # 解析完整链路
ls -li FILE # inode;判断硬链接是否指向同一 inode
  • mtime:文件内容最后修改时间。
  • ctime:内容或权限、所有者等元数据最后变更时间,不是创建时间。
  • atime:最后访问时间,可能因挂载策略而延迟或不更新。
  • Birth/btime:创建时间,并非所有文件系统都支持。

文件与目录操作

列出与查看

1
2
3
4
5
6
7
8
9
ls -lah                        # 全部项目、详细信息、易读大小
ls -ld DIR # 看目录本身,不展开目录内容
ls -lt # 按修改时间排序
tree -a -L 2 DIR # 两层目录树,需安装 tree
cat FILE # 小文件全文
less FILE # 大文件分页;/ 搜索,n 下一个,q 退出
head -n 20 FILE # 前 20 行
tail -n 20 FILE # 后 20 行
tail -F FILE # 持续跟踪,适合日志

创建、复制、移动与删除

1
2
3
4
5
6
7
8
9
10
11
touch FILE                     # 文件不存在则创建;存在则更新时间戳
mkdir -p DIR/SUBDIR # 中间目录不存在时一并创建
cp FILE DEST # 复制文件
cp -a DIR DEST # 复制目录并尽量保留链接、权限、时间等
cp -i SOURCE DEST # 覆盖前确认
mv -i SOURCE DEST # 移动/重命名,覆盖前确认
ln SOURCE HARD_LINK # 硬链接:同一文件数据、同一 inode
ln -s TARGET SOFT_LINK # 软链接:保存目标路径,可跨文件系统
rm -i FILE # 删除文件
rmdir EMPTY_DIR # 只删除空目录
rm -rI DIR # 递归删除,执行前进行一次确认

注意:

  • rm 通常不经过回收站;重要数据应先备份。
  • 命令参数可能以 - 开头时,用 -- 结束选项:rm -- -strange-name
  • 使用通配符前先预览:先执行 printf '%s\n' ./*.log,确认后再操作。zsh 在没有匹配项时默认报错并且不执行命令;Bash 默认会把未匹配的模式原样传给命令。
  • 不要用 mv *.[ch] DIR 期待匹配子目录;普通 glob 默认只匹配当前层。

通配符 glob

模式 匹配
* 任意长度字符串,不含路径分隔符/
? 任意单个字符
[abc] abc 中任一字符
[0-9] 一个数字字符
*.[ch] .c.h 结尾的当前层文件

glob 由 Shell 在命令执行前展开;正则表达式由 grepsed 等程序解释,两者不要混淆。

zsh 与 Bash 的未匹配 glob 处理不同:

1
2
3
print -rl -- ./*.log(N)         # zsh:N 使本次模式无匹配时展开为空
setopt NULL_GLOB # zsh:此后所有无匹配模式都展开为空
unsetopt NULL_GLOB # 恢复默认的 NOMATCH 报错行为
1
2
3
shopt -s nullglob               # Bash:无匹配模式展开为空
printf '%s\n' ./*.log
shopt -u nullglob
  • zsh 默认 NOMATCH 更安全,能阻止把字面量 *.log 交给命令;一般优先使用局部限定符 (N),不要长期全局开启 NULL_GLOB
  • Bash 的 shopt 是 Bash 专属;zsh 使用 setopt/unsetopt,不能照搬。
  • zsh 可用 **/*.log 递归匹配;Bash 需要先执行 shopt -s globstar 才具有同类行为。跨 Shell 脚本优先使用 find

搜索文件与统计空间

find 常用模板

1
2
3
4
5
6
7
8
find DIR -type f -name '*.log'                 # 按名称找文件
find DIR -type d -name node_modules -prune # 找目录
find DIR -type f -iname '*.jpg' # 忽略大小写
find DIR -type f -size +1G # 大于 1 GiB
find DIR -type f -mtime -7 # 7 天内修改
find DIR -type f -empty # 空文件
find DIR -type f -perm /111 # 任一执行位已设置
find DIR -type f -exec sha256sum {} + # 批量作为参数执行

安全批处理原则:优先使用 -exec ... {} + 或 NUL 分隔,不要把文件名直接拼进未经引用的 for 循环。

1
find DIR -type f -print0 | xargs -0 COMMAND

磁盘空间

1
2
3
4
5
6
df -hT                          # 各文件系统容量、类型和挂载点
df -i # inode 使用率
du -sh DIR # 目录总大小
du -xh --max-depth=1 DIR | sort -h
find DIR -xdev -type f -size +1G -printf '%s %p\n' | sort -n
ncdu DIR # 交互式查看,需安装;删除功能要谨慎

判断“磁盘满”的两条线:

  1. df -h 的空间是否 100%。
  2. df -i 的 inode 是否 100%;大量小文件会耗尽 inode,即使容量还有剩余。

du 合计明显小于 df 已用空间,检查已删除但仍被进程打开的文件:

1
sudo lsof +L1

查看、搜索与加工文本

标准流与重定向

文件描述符 默认位置
stdin 0 键盘输入
stdout 1 正常输出到终端
stderr 2 错误输出到终端
1
2
3
4
5
6
7
8
9
10
COMMAND > FILE                  # 覆盖 stdout
COMMAND >> FILE # 追加 stdout
COMMAND 2> ERROR.log # 覆盖 stderr
COMMAND > OUT.log 2>&1 # stdout 与 stderr 写入同一文件
COMMAND &> ALL.log # zsh 与 Bash 均支持;非 POSIX
COMMAND < INPUT # 从文件提供 stdin
COMMAND1 | COMMAND2 # 前者 stdout 接后者 stdin
COMMAND | tee FILE # 同时显示并覆盖写文件
COMMAND | tee -a FILE # 同时显示并追加
COMMAND > /dev/null 2>&1 # 丢弃全部输出

若脚本还需要兼容 /bin/sh,使用 COMMAND > ALL.log 2>&1,不要使用 &>

sudo COMMAND > /root/file 仍可能权限不足,因为 > 由当前普通 Shell 执行。正确写法:

1
COMMAND | sudo tee /ROOT/FILE >/dev/null

grep 搜索

1
2
3
4
5
6
7
8
9
grep -n 'TEXT' FILE             # 行号
grep -i 'TEXT' FILE # 忽略大小写
grep -v 'TEXT' FILE # 反选
grep -w 'word' FILE # 完整单词
grep -x 'whole line' FILE # 整行匹配
grep -E 'foo|bar' FILE # ERE 扩展正则
grep -F 'a.b[c]' FILE # 按纯文本,不解释正则
grep -C 3 'ERROR' FILE # 前后各 3 行上下文
grep -RIn --exclude-dir=.git 'TODO' DIR

sort、uniq、cut、tr、wc

1
2
3
4
5
6
7
8
9
10
11
wc -l FILE                      # 行数
wc -w FILE # 单词数
cut -d: -f1 /etc/passwd # 以 : 分列,取第 1 列
tr '[:lower:]' '[:upper:]' # 转大写,从 stdin 读取
sort FILE # 字典序
sort -n FILE # 数值升序
sort -nr FILE # 数值降序
sort -k2,2nr FILE # 第 2 列数值降序
sort -u FILE # 排序并去重
sort FILE | uniq -c # 统计连续重复行,先排序再统计
LC_ALL=C sort FILE # 固定为逐字节、可复现的排序规则

不要使用 sort FILE > FILE,Shell 会在 sort 读取前清空文件。使用:

1
sort -o FILE FILE

sed 行级编辑

1
2
3
4
5
sed -n '10,20p' FILE                           # 输出第 10 到 20 行
sed '2d' FILE # 删除第 2 行后输出
sed -E 's/foo|bar/baz/g' FILE # 替换,先不写回
sed -i.bak 's|old/url|new/url|g' FILE # 写回并保留 .bak
sed '/^#/d; /^[[:space:]]*$/d' FILE # 去注释与空行

s 可使用 | 等字符作分隔符,处理 URL 时比 / 清晰。写配置时尽量匹配完整键值,使操作可重复执行:

1
sed -i.bak 's|^homepage=.*$|homepage=https://example.com/|' FILE

awk 列处理

1
2
3
4
5
awk '{print $1}' FILE                           # 第 1 列;$0 为整行
awk -F: '{print $1, $7}' /etc/passwd # 自定义输入分隔符
awk '$3 > 0 {print $1, $2 * $3}' FILE # 条件与计算
awk '{sum += $2} END {print sum}' FILE # 求和
awk '{count[$1]++} END {for (k in count) print count[k], k}' FILE

日志统计模板:按第 1 列分组、累计第 10 列、取前 50:

1
2
3
4
awk '$10 ~ /^[0-9]+$/ {bytes[$1] += $10}
END {for (ip in bytes) print ip, bytes[ip]}' access.log |
sort -k2,2nr |
head -n 50

diff 比较

1
2
3
4
diff -u OLD NEW                 # 统一格式差异,最常用
diff -ru DIR1 DIR2 # 递归比较目录
cmp FILE1 FILE2 # 字节级判断是否一致
sha256sum FILE # 内容校验值

正则表达式速查

常用 ERE 元字符

表达式 含义
^$ 行首、行尾
. 任意单个字符
[abc][^abc] 字符集合、排除集合
[0-9] 数字字符
*+? 前项重复 0+、1+、0/1 次
{n}{n,m} 精确 n 次、n 至 m 次
(abc) 分组
a | b
\ 转义元字符
1
2
3
4
^[1-9][0-9]*$                                  # 正整数
^[A-Za-z]+$ # 仅英文字母
^(Chapter|Section) [1-9][0-9]?$ # Chapter/Section 1..99
^[^@[:space:]]+@[^@[:space:]]+\.[^@[:space:]]+$ # 简化邮箱格式
  • grep、普通 sed 默认使用 BRE;优先加 -E 使用更直观的 ERE。
  • awk 使用 ERE。
  • PCRE、Python、JavaScript 等现代正则支持非贪婪、环视、命名组等,不能直接假设 POSIX 工具也支持。
  • 回溯型引擎中避免 (a+)+ 一类嵌套、重叠量词,以免灾难性回溯或 ReDoS。
  • 解析任意嵌套语法、HTML、JSON 时优先使用对应解析器,不要强行使用正则。

权限、用户与用户组

读懂权限

1
2
3
4
-rwxr-x--- 1 alice dev 4096 Jul 22 10:00 script.sh
│└─┬┘└─┬┘└─┬┘ │ │
│ owner group other 所有者 组
└ - 普通文件;d 目录;l 软链接
权限 文件 目录
r 读内容 列出目录项名称
w 改内容 新建、删除、重命名目录项
x 作为程序执行 进入目录、访问其中项目

数字权限:r=4w=2x=1,每组三项相加。

1
2
3
4
5
6
7
8
9
chmod u+x SCRIPT                # 所有者加执行权限
chmod go-rw FILE # 组和其他人移除读写
chmod 640 FILE # rw-r-----
chmod 750 DIR # rwxr-x---
chmod -R u=rwX,go=rX DIR # X 只给目录和原本可执行文件加 x
chown USER:GROUP FILE # 修改所有者与组
chown -R USER:GROUP DIR # 递归修改,目标必须反复确认
chgrp GROUP FILE # 只改组
umask # 查看新文件权限掩码

常见安全默认值:普通文件 644、私密文件 600、普通目录 755、私密目录 700。不要用 chmod 777 粗暴解决问题,应判断谁需要哪种权限。

身份与提权

1
2
3
4
5
6
7
8
id                              # 当前身份
groups USER # 用户所属组
sudo COMMAND # 以 root 执行单条命令
sudo -u USER COMMAND # 以指定用户运行
sudo -i # root 登录式 Shell;用完 exit
su - USER # 切换并加载目标用户登录环境
passwd # 修改自己的密码
sudo passwd USER # 管理员修改指定用户密码

Debian/Ubuntu 用户管理:

1
2
3
4
5
sudo adduser USER
sudo addgroup GROUP
sudo adduser USER GROUP
sudo usermod -aG GROUP USER # -a 不可漏,否则可能覆盖附加组
sudo deluser USER GROUP

加入新组后需重新登录,或临时执行 newgrp GROUPdocker 组近似拥有 root 权限,不应随意添加用户。

特殊权限:

  • setuid:程序以文件所有者的有效 UID 运行。
  • setgid:程序以文件所属组运行;目录内新文件继承该目录组。
  • sticky:目录内文件通常只能由文件所有者、目录所有者或 root 删除;典型为 /tmp

软件包管理

Debian / Ubuntu(APT)

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo apt update                     # 刷新可用软件列表
apt search KEYWORD # 搜索
apt show PACKAGE # 包详情
apt policy PACKAGE # 已装/候选版本与来源
sudo apt install PACKAGE...
sudo apt remove PACKAGE # 删除程序,通常保留系统级配置
sudo apt purge PACKAGE # 连系统级配置一起删除
sudo apt autoremove # 删除不再需要的自动依赖,先审阅列表
sudo apt upgrade # 更新已安装包
dpkg -l | grep PATTERN # 查询已安装包
dpkg -L PACKAGE # 包安装了哪些文件
dpkg -S /PATH/TO/FILE # 哪个包提供该文件
sudo apt install ./PACKAGE.deb # 安装本地 deb 并处理依赖
  • 脚本中倾向使用命令行接口更稳定的 apt-get,交互操作使用 apt 更直观。
  • apt update 只更新软件列表,apt upgrade 才更新软件。
  • 第三方源必须核验发行版代号、架构、签名密钥和官方说明;不要使用已废弃的 apt-key 流程。
  • 安装失败先看是否有另一个包管理进程,不要直接删除 lock 文件。

发行版速查

系列 搜索 安装 更新系统 删除
Debian/Ubuntu apt search X sudo apt install X sudo apt update && sudo apt upgrade sudo apt remove X
Fedora/RHEL dnf search X sudo dnf install X sudo dnf upgrade sudo dnf remove X
Arch pacman -Ss X sudo pacman -S X sudo pacman -Syu sudo pacman -Rns X
openSUSE zypper search X sudo zypper install X sudo zypper update sudo zypper remove X

从源码安装

典型 Autotools 流程:

1
2
3
4
5
tar xf SOURCE.tar.xz
cd SOURCE
./configure --prefix=/usr/local
make -j"$(nproc)"
sudo make install

优先选择发行版包、项目官方包或可追踪的构建方式。make install 往往不便卸载;执行前查看 README、INSTALL、构建依赖与安装前缀。

归档与压缩

tar

1
2
3
4
5
6
7
tar caf archive.tar.gz DIR FILE       # 按后缀自动选择压缩算法
tar tf archive.tar.gz # 先查看内容
mkdir -p DEST
tar xf archive.tar.gz -C DEST # 解压到指定目录
tar czf archive.tar.gz DIR # gzip
tar cJf archive.tar.xz DIR # xz
tar --zstd -cf archive.tar.zst DIR # zstd

记忆:c create、x extract、t list、f archive file、C target directory、v verbose、a auto-compress。

安全解压陌生归档前先 tar tf,警惕绝对路径、../ 路径穿越、覆盖现有文件和解压炸弹;最好解压到新建空目录。

其他格式

1
2
3
4
5
zip -r archive.zip DIR
unzip -l archive.zip # 列表
unzip archive.zip -d DEST
7z l archive.7z
7z x archive.7z -oDEST

进程、任务与会话

查看进程

1
2
3
4
5
6
7
ps aux                              # 全系统快照
ps -ef --forest # 父子关系
ps -p PID -o pid,ppid,user,stat,%cpu,%mem,etime,cmd
pgrep -a NAME # 按名称找 PID 并显示命令行
pstree -ap # 进程树
top # 动态查看
htop # 更友好的交互界面,通常需安装

常见状态:R 运行、S 可中断睡眠、D 不可中断 I/O、T 停止、Z 僵尸。僵尸进程已经退出,需由父进程回收;对它 kill -9 没有意义。

前后台任务

1
2
3
4
5
6
COMMAND &                           # 直接在后台启动
jobs -l # 当前 Shell 的任务
Ctrl+Z # 暂停前台任务
bg %1 # 让任务 1 在后台继续
fg %1 # 将任务 1 带回前台
disown %1 # 从当前 Shell 任务表移除

jobs 只显示当前 Shell 启动的任务,不等于系统所有进程。

信号与结束进程

1
2
3
4
5
6
kill PID                            # 默认 SIGTERM,允许清理后退出
kill -INT PID # 类似前台 Ctrl+C
kill -HUP PID # 部分程序用它重载配置
kill -KILL PID # 最后手段,无法捕获和清理
pkill -TERM -x NAME # 精确进程名
kill -l # 信号列表

推荐顺序:确认目标 → TERM → 等待并复查 → 查日志/状态 → 必要时 KILLD 状态进程要等内核调用返回;PID 1 对某些信号有特殊保护。

优先级

1
2
nice -n 10 COMMAND                  # 以较低调度优先级启动
renice -n 10 -p PID # 调整运行中进程

nice 值通常为 -20..19,值越大优先级越低;提高优先级通常需要权限。

nohup 与 tmux

1
2
3
4
5
6
7
nohup COMMAND >app.log 2>&1 &       # 简单非交互临时任务
echo $! > app.pid # 保存最近后台 PID

tmux new -s work # 新建命名会话
tmux ls # 列出会话
tmux attach -t work # 恢复会话
tmux kill-session -t work # 结束会话

tmux 默认前缀 Ctrl+Bd 脱离、c 新窗口、% 左右分屏、" 上下分屏、方向键切换面板、z 面板缩放。

systemd 服务与日志

管理服务

1
2
3
4
5
6
7
8
9
10
11
12
systemctl status SERVICE --no-pager -l
sudo systemctl start SERVICE
sudo systemctl stop SERVICE
sudo systemctl restart SERVICE
sudo systemctl reload SERVICE # 服务支持时,仅重载配置
sudo systemctl enable SERVICE # 设置开机启动,不立即启动
sudo systemctl enable --now SERVICE # 设置并立即启动
sudo systemctl disable --now SERVICE
systemctl is-active SERVICE
systemctl is-enabled SERVICE
systemctl --failed
sudo systemctl daemon-reload # unit 文件变化后执行

日志

1
2
3
4
5
6
7
journalctl -u SERVICE                    # 指定服务全部日志
journalctl -u SERVICE -b # 本次启动
journalctl -u SERVICE -f # 实时跟踪
journalctl -u SERVICE --since '1 hour ago'
journalctl -b -1 # 上一次启动
journalctl -p err -b # 本次启动 error 及更严重日志
journalctl --disk-usage # journal 占用

自定义 service 模板

保存为 /etc/systemd/system/myapp.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=My Application
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

应用并验证:

1
2
3
4
5
sudo systemd-analyze verify /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service
journalctl -u myapp.service -f

ExecStart 使用绝对路径;不要默认用 root 运行应用;秘密信息不宜直接写进全局可读 unit 文件。

systemd 的 ExecStart= 默认不经过交互 Shell,|>&&、glob、alias、~ 等不会按 zsh/Bash 语法解析。确实需要 Shell 表达式时显式写为 ExecStart=/usr/bin/zsh -c '...';Bash 对应 /bin/bash -c '...',但能直接执行程序时仍应避免额外套一层 Shell。

定时任务

一次性任务 at

1
2
3
echo '/path/to/job.sh >>/path/to/job.log 2>&1' | at now + 10 minutes
atq # 列表
atrm JOB_ID # 删除

需要 atd 服务运行。任务没有交互终端,输出应显式写入日志。

周期任务 cron

1
2
3
4
crontab -e                           # 编辑当前用户任务
crontab -l # 列出
crontab -l > crontab.backup # 修改前备份
crontab -r # 删除全部,危险且与 -e 相邻
1
2
3
# 分 时 日 月 周 命令
*/15 * * * * /absolute/path/job.sh >>/absolute/path/job.log 2>&1
5 3 * * * /absolute/path/backup.sh

cron 环境很精简:使用绝对路径、显式设置 PATH、不要依赖当前目录或交互式 Shell 配置。

cron 通常不会读取 ~/.zshrc~/.bashrc,命令常由 /bin/sh 解释。确实需要 Shell 专属语法时,在 crontab 顶部明确指定其一:

1
2
SHELL=/usr/bin/zsh
# Bash 对应:SHELL=/bin/bash

更稳妥的方式是让 cron 只执行带有正确 shebang 的独立脚本,避免把复杂 zsh/Bash 语法直接写在 crontab 中。防止耗时任务重叠:

1
*/5 * * * * flock -n /tmp/myjob.lock /absolute/path/job.sh

复杂任务优先考虑 systemd timer:有统一日志、依赖管理、错过任务补跑和防重叠能力。

网络、下载与远程连接

分层排障

1
2
3
4
5
6
7
8
ip -br link                         # 网卡是否 UP
ip -br addr # 是否取得地址
ip route # 是否有默认路由
ping -c 4 GATEWAY # 到网关
ping -c 4 1.1.1.1 # 到公网 IP
getent hosts example.com # 系统解析器能否解析 DNS
curl -I --connect-timeout 5 https://example.com
ss -lntup # 服务是否监听、监听在哪个地址

判断思路:

  1. 无网卡或未 UP:检查链路、虚拟机网卡、驱动。
  2. 无 IP:检查 DHCP 或静态地址配置。
  3. 无默认路由:检查网关。
  4. 公网 IP 可通、域名不通:DNS 问题。
  5. 域名可解析、端口不通:服务、监听地址、防火墙或上游网络问题。
  6. HTTP 失败:用 curl -v 查看连接、TLS、重定向和响应状态。

wget 与 curl

1
2
3
4
5
6
7
8
9
10
11
wget URL                              # 下载,保留服务端文件名
wget -O FILE URL # 指定文件名
wget -c URL # 断点续传,需服务端支持 Range
wget --content-disposition URL # 参考响应头决定文件名

curl -fLO URL # 失败时非零退出;保留远端文件名
curl -fsSL URL -o FILE # 静默、跟随跳转、失败即报错
curl -I URL # 仅响应头
curl -v URL # 详细诊断
curl -H 'Header: Value' URL # 请求头
curl -X POST -H 'Content-Type: application/json' -d '{"k":"v"}' URL

SSH

1
2
3
4
5
ssh USER@HOST                         # 登录
ssh -p PORT USER@HOST # 指定端口
ssh -i ~/.ssh/id_ed25519 USER@HOST # 指定私钥
ssh -v USER@HOST # 一级详细诊断;可用 -vvv
ssh USER@HOST 'COMMAND' # 执行远程命令

生成和部署密钥:

1
2
3
4
5
ssh-keygen -t ed25519 -C 'DEVICE-NAME'
ssh-copy-id -i ~/.ssh/id_ed25519.pub USER@HOST
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

~/.ssh/config 模板:

1
2
3
4
5
6
Host myserver
HostName 203.0.113.10
User alice
Port 22
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60

随后可直接 ssh myserver。首次连接应通过可信渠道核对主机指纹,不要习惯性忽略警告。

文件传输

1
2
3
4
5
scp FILE USER@HOST:/remote/path/
scp -r DIR USER@HOST:/remote/path/
rsync -avh --progress DIR/ USER@HOST:/remote/path/
rsync -avhn --delete SOURCE/ DEST/ # 先 dry-run
rsync -avh --delete SOURCE/ DEST/ # --delete 会删目标多余文件,危险

SOURCE/ 表示复制目录内容,SOURCE 表示连目录本身一起复制,这是 rsync 最容易混淆的细节之一。

zsh 交互与脚本(兼顾 Bash)

启动文件与默认 Shell

场景 zsh Bash
所有 Shell ~/.zshenv,只放必须的环境变量 无完全对应的用户文件
登录 Shell ~/.zprofile,之后还会读 ~/.zlogin ~/.bash_profile~/.bash_login~/.profile 中第一个存在的文件
交互式非登录 Shell ~/.zshrc ~/.bashrc
退出登录 Shell ~/.zlogout ~/.bash_logout

日常 alias、补全、提示符和交互选项放在 ~/.zshrc;环境变量通常放在 ~/.zshenv~/.zprofile.zshenv 会被每个 zsh 脚本读取,必须保持精简且不能输出文字。

  • zsh 的交互式登录会依次读取 .zshenv.zprofile.zshrc.zlogin
  • Bash 登录时只读取三个登录文件中第一个存在者,且不会自动读取 .bashrc;通常在 .bash_profile 中显式 source ~/.bashrc
1
2
3
4
sudo apt install zsh                 # Debian/Ubuntu 尚未安装时
chsh -s "$(command -v zsh)" # 将登录 Shell 改成 zsh,重新登录后生效
printf '%s\n' "$SHELL" # 账户登记的默认 Shell
printf '%s\n' "$0" # 当前 Shell 名;登录 Shell 可能带前导 -

$SHELL 不一定等于当前正在运行的 Shell;用 ps -p $$ -o comm=printf '%s\n' "$ZSH_VERSION" 确认当前是否为 zsh,Bash 对应 $BASH_VERSION

命令组合

写法 zsh 与 Bash 中的行为
A; B 无论 A 成败都执行 B
A && B A 成功才执行 B
A | | |A &| A 放入后台 | |{ A; B; }| 当前 Shell 中成组执行,花括号旁要留空格 | |(A; B)` 子 Shell 中执行,不影响当前目录和变量

退出码 0 表示成功,非 0 表示失败;两种 Shell 都可用 $? 读取上一条命令退出码。zsh 还提供更易读的 $status

管道各段退出码写法不同:

1
print -r -- $pipestatus          # zsh:数组,默认下标从 1 开始
1
printf '%s\n' "${PIPESTATUS[@]}" # Bash:数组,默认下标从 0 开始

变量、引用与单词拆分

1
2
3
4
5
6
7
8
name='Linux'                       # 等号两边不能有空格
printf '%s\n' "$name"
printf '%s\n' "${name}101" # 花括号明确变量边界
unset name
export APP_ENV=prod # 导出给子进程
APP_ENV=test COMMAND # 仅本次命令有效
result=$(COMMAND) # 命令替换
value=$((2 + 3 * 4)) # 算术展开

引用规则:

  • 单引号 '...':内容完全按字面值。
  • 双引号 "...":保留空格,同时展开变量和 $(...)
  • 新脚本使用 $(COMMAND),不要使用难嵌套的反引号。
  • 无论使用 zsh 还是 Bash,变量和命令替换默认都加双引号;这是最稳妥、最易移植的写法。

两者最重要的差异之一是未加引号的标量展开:

1
2
3
words='one two'
printf '<%s>\n' $words # zsh 默认作为一个参数:<one two>
printf '<%s>\n' ${=words} # zsh:显式按 IFS 拆分成两个参数
1
2
3
words='one two'
printf '<%s>\n' $words # Bash 默认拆成 <one> 和 <two>
printf '<%s>\n' "$words" # 两者通用:保持一个参数

不要为了模仿 Bash 而在 zsh 中全局开启 SH_WORD_SPLIT;需要拆分时优先使用数组或 zsh 的 ${=name} 局部标志。

zsh 将 $path 暴露为与 $PATH 自动同步的数组:

1
2
path=(~/.local/bin $path)        # zsh:前置目录并自动更新 PATH
typeset -U path # zsh:数组元素去重并保留首次出现项
1
2
PATH="$HOME/.local/bin:$PATH"    # Bash:直接修改字符串 PATH
export PATH

数组

普通数组的下标是常见陷阱:zsh 默认从 1 开始,Bash 从 0 开始。

1
2
3
4
items=(alpha beta gamma)
print -r -- "$items[1]" # alpha
print -rl -- "${items[@]}" # 每个元素一行
items+=(delta)
1
2
3
4
items=(alpha beta gamma)
printf '%s\n' "${items[0]}" # alpha
printf '%s\n' "${items[@]}" # 每个元素一行
items+=(delta)

关联数组声明也不同:zsh 使用 typeset -A map,Bash 使用 declare -A map。跨 Shell 脚本若大量依赖数组,应明确选择解释器,不要假设能直接互换。

read 输入

两种 Shell 的提示参数有冲突:zsh 的 read -p 表示从 coprocess 读取,不是显示 prompt。

1
2
3
4
5
IFS= read -r "name?Name: "       # zsh:变量名与 ?提示文字写在同一参数
printf 'Hello, %s\n' "$name"

IFS= read -rs "password?Password: "
printf '\n'
1
2
3
4
5
IFS= read -r -p 'Name: ' name    # Bash:-p 后跟提示,变量名放最后
printf 'Hello, %s\n' "$name"

IFS= read -rs -p 'Password: ' password
printf '\n'
  • 两者都建议使用 -r,避免反斜杠被当作转义符。
  • 两者的 -s 都表示静默输入,适合密码;不要保存或打印明文密码。
  • 读入数组时 zsh 使用 read -A items,Bash 使用 read -a items
  • 格式化输出优先使用 printfecho 是否解释反斜杠和选项会受 Shell 与配置影响。

逐行读取文件的下列写法在两者中通用:

1
2
3
while IFS= read -r line; do
printf '%s\n' "$line"
done < FILE

避免用 COMMAND | while ... 保存循环变量:zsh 默认在当前 Shell 执行管道最后一段,而 Bash 通常在子 Shell 中执行,循环后的变量结果会不同。使用上面的输入重定向,或使用 done < <(COMMAND)

脚本参数

写法 zsh 与 Bash 中的含义
$0 脚本名
$1$2 第 1、2 个参数
$# 参数个数
"$@" 每个参数保持独立,遍历参数时首选
$? 上条命令退出码
$$ 当前 Shell PID
$! 最近后台进程 PID

推荐 zsh 脚本骨架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env zsh

set -euo pipefail

usage() {
printf 'Usage: %s INPUT OUTPUT\n' "$0" >&2
}

main() {
if (( $# != 2 )); then
usage
return 2
fi

local input=$1
local output=$2

[[ -f "$input" ]] || {
printf 'Error: not a regular file: %s\n' "$input" >&2
return 1
}

cp -- "$input" "$output"
}

main "$@"

Bash 版本的主体相同,只需改 shebang 和严格模式:

1
2
3
4
#!/usr/bin/env bash

set -Eeuo pipefail
# 后续主体与上面的 zsh 版本相同

zsh 的 set -E 与 Bash 的 set -E 并非同一语义,因此 zsh 骨架不照搬大写 E。严格模式适合新写的受控脚本,但不是魔法:预期失败的探测命令、条件判断和旧脚本可能需要单独处理。

条件与循环

以下写法在 zsh 与 Bash 中通用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[[ -f "$file" ]]                  # 普通文件
[[ -d "$dir" ]] # 目录
[[ -r "$file" && -s "$file" ]] # 可读且非空
[[ -z "$value" ]] # 空字符串
[[ "$a" == "$b" ]] # 字符串相等
(( count >= 10 )) # 数值比较

if [[ -f "$file" ]]; then
printf 'exists\n'
else
printf 'missing\n' >&2
fi

for file in "$@"; do
printf '%s\n' "$file"
done

[[ ... ]][ ... ] 更适合明确选择 zsh/Bash 的脚本,但不属于 POSIX sh。若脚本 shebang 是 #!/bin/sh,应改用 [ ... ] 并遵守 POSIX 语法。

执行、加载与语法检查

1
2
3
4
zsh script.zsh                  # zsh:新进程执行,无需执行权限
zsh -n script.zsh # 只检查语法
zsh -x script.zsh # 跟踪执行;可能泄露秘密变量
source ~/.zshrc # 当前 zsh 重新加载交互配置
1
2
3
4
5
bash script.sh                  # Bash:新进程执行,无需执行权限
bash -n script.sh # 只检查语法
bash -x script.sh # 跟踪执行;可能泄露秘密变量
source ~/.bashrc # 当前 Bash 重新加载交互配置
shellcheck script.sh # 主要检查 POSIX sh/Bash,不应当作 zsh 完整验证器

两者通用:

1
2
3
4
5
6
chmod +x SCRIPT
./SCRIPT # 根据 shebang 选择解释器
source SCRIPT # 当前 Shell 执行,会改变变量、选项和目录
. .venv/bin/activate # source 的常见合理用途
type COMMAND # alias、函数、内建还是外部程序
command -V COMMAND

新脚本应由 shebang 明确解释器,不要用 sh zsh-script.zshbash zsh-script.zsh 强行运行另一种 Shell 的脚本。

C/C++ 与 Python 开发

C/C++

1
2
3
4
5
6
7
8
gcc -Wall -Wextra -O2 main.c -o main
g++ -Wall -Wextra -O2 main.cpp -o main
gcc -c main.c -o main.o
gcc -c util.c -o util.o
gcc main.o util.o -o app
gcc thread.c -o thread -lpthread
ldd ./app # 动态库依赖;不要对不可信程序使用 ldd
file ./app # 架构、链接类型

Makefile 最小模板,命令行必须用 Tab 缩进:

1
2
3
4
5
6
7
8
9
10
11
12
CC := gcc
CFLAGS := -Wall -Wextra -O2

app: main.o util.o
$(CC) $^ -o $@

main.o: main.c util.h
util.o: util.c util.h

.PHONY: clean
clean:
rm -f app *.o
1
2
3
make                             # 构建第一个目标
make -j"$(nproc)" # 并行构建
make clean

动态链接文件小且能共享、统一更新库;静态链接较易分发但文件更大,依赖修复需重新构建。跨架构程序出现 Exec format error 时,用 file PROGRAM 检查目标架构。

Python 虚拟环境

1
2
3
4
5
6
7
8
python3 --version
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install PACKAGE
python -m pip install -r requirements.txt
python -m pip freeze > requirements.lock.txt
deactivate
  • 使用 python -m pip 可确保 pip 属于当前解释器。
  • 每个项目独立使用 .venv,不要用 sudo pip install 修改系统 Python。
  • requirements.txt 可写 packagepackage>=1.2package==1.2.3;越精确越易复现,越宽松越易升级。

Conda

1
2
3
4
5
conda create -n PROJECT python=3.11
conda activate PROJECT
conda deactivate
conda env export --from-history > environment.yml
conda env create -f environment.yml

Conda 还能管理非 Python 二进制依赖;同一环境混用 Conda 与 pip 时,通常先用 Conda 安装,再用 pip 补充,并记录环境文件。

Docker 速查

概念

对象 含义 查看命令
Registry 存储和分发镜像 docker search
Image 只读文件系统模板 docker image ls
Container 镜像的运行实例 docker ps -a
Dockerfile 可复现的镜像构建说明 docker build
Volume / bind mount 持久化或映射数据 docker volume ls

容器与镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run --rm hello-world
docker run --rm -it ubuntu:24.04 bash
docker run -d --name web -p 8080:80 IMAGE
docker run --rm -v "$PWD:/workspace" -w /workspace IMAGE COMMAND
docker ps # 正在运行
docker ps -a # 全部容器
docker logs -f --tail 100 CONTAINER
docker exec -it CONTAINER bash # 新建调试 Shell
docker inspect CONTAINER
docker stop CONTAINER
docker rm CONTAINER
docker image ls
docker pull IMAGE:TAG
docker build -t NAME:TAG .
docker image rm IMAGE

要点:

  • docker run ... bash 中的 bash 是容器内要启动的程序,与主机默认使用 zsh 无关;精简镜像可能只有 sh,也可能额外安装了 zsh。
  • -i 保持 stdin,-t 分配伪终端,交互 Shell 常用 -it
  • --rm 退出即删容器,适合一次性任务。
  • -p 主机端口:容器端口 发布端口。
  • -v 主机绝对路径:容器路径 绑定目录;写操作会影响主机。
  • 生产环境固定镜像版本或 digest,不依赖会漂移的 latest
  • 容器删除后,未放入 volume 或 bind mount 的可写层数据会丢失。

Dockerfile 最小模板

1
2
3
4
5
6
7
8
9
10
11
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

RUN useradd --create-home appuser
USER appuser

CMD ["python", "app.py"]

构建原则:固定合理版本、先复制依赖清单利用缓存、使用 .dockerignore、不要把密钥打进镜像、尽量非 root 运行、合并并清理包管理缓存。

Compose

1
2
3
4
5
6
7
8
docker compose up -d
docker compose ps
docker compose logs -f --tail 100
docker compose exec SERVICE sh
docker compose restart SERVICE
docker compose down # 删容器和网络,默认保留命名卷
docker compose down -v # 连卷一起删,数据可能不可恢复
docker compose config # 展开并验证配置

Docker 故障入口

1
2
3
4
5
6
7
systemctl status docker
journalctl -u docker -b
docker info
docker inspect CONTAINER
docker logs CONTAINER
docker stats
docker system df

权限提示:加入 docker 组等价于授予接近 root 的控制能力;不要把 Docker socket 暴露给不可信用户或容器。

高频故障排查手册

command not found

1
2
3
4
type -a COMMAND
command -v COMMAND
printf '%s\n' "$PATH" | tr : '\n'
apt search COMMAND

检查顺序:拼写 → 软件是否安装 → 当前 Shell 是否加载新 PATH → 可执行文件目录是否在 PATH → 是否实际需要 ./program

刚安装程序或修改 PATH 后仍找不到命令时,刷新命令缓存:zsh 使用 rehash,Bash 使用 hash -r。重新打开 Shell 也会重建缓存。

Permission denied

1
2
3
4
id
namei -l /full/path/to/file # 逐层查看目录权限
ls -ld /full /full/path /full/path/to/file
mount | grep ' /relevant/mount '

可能原因:文件缺 x、父目录缺 x、目标目录缺 w+x、所有者/组不符、文件系统只读或 noexec、SELinux/AppArmor 策略。不要先上 chmod 777

程序无法执行

1
2
3
4
file PROGRAM
ls -l PROGRAM
head -n 1 SCRIPT
ldd PROGRAM
  • Permission denied:检查执行位、目录权限、noexec
  • Exec format error:架构不匹配或脚本 shebang 缺失/错误。
  • bad interpreter:解释器路径不存在,或脚本带 Windows CRLF;可用 file SCRIPT 检查。
  • error while loading shared libraries:用 lddnot found,安装对应架构的软件包;不要随意从网络复制 .so

服务启动失败

1
2
3
4
5
6
systemctl status SERVICE --no-pager -l
journalctl -u SERVICE -b -n 200 --no-pager
systemctl cat SERVICE
systemctl show SERVICE -p User,Group,ExecStart,WorkingDirectory,Environment
sudo systemd-analyze verify /etc/systemd/system/SERVICE.service
ss -lntup

检查:配置语法 → 命令绝对路径 → 运行用户权限 → 工作目录 → 环境变量 → 端口冲突 → 依赖服务 → 磁盘空间。

端口已被占用

1
2
3
sudo ss -lntup 'sport = :PORT'
sudo lsof -nP -iTCP:PORT -sTCP:LISTEN
ps -fp PID

确认进程身份后,优先通过对应服务管理器停止,不要直接杀未知进程。

网络或 DNS 异常

1
2
3
4
5
6
7
ip -br addr
ip route
ping -c 4 GATEWAY
ping -c 4 1.1.1.1
getent hosts example.com
cat /etc/resolv.conf
curl -v --connect-timeout 5 https://example.com

“IP 通、域名不通”通常指向 DNS;“本机监听但外部不通”继续查监听地址是否为 127.0.0.1、主机防火墙、云安全组、NAT/端口映射。

CPU 或内存异常

1
2
3
4
5
6
7
uptime
top
ps aux --sort=-%cpu | head -n 15
ps aux --sort=-%mem | head -n 15
free -h
vmstat 1
journalctl -k -b | grep -iE 'oom|killed process'

高 load 不必然等于高 CPU,也可能是大量 D 状态 I/O 等待。发现 OOM 时先找内存增长来源,再考虑限额或扩容,不要只清缓存掩盖问题。

磁盘写不进去

1
2
3
4
5
6
df -hT
df -i
du -xh --max-depth=1 /relevant/path | sort -h
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /relevant/path
sudo lsof +L1
dmesg -T | tail -n 100

区分:容量满、inode 满、只读挂载、配额、权限、删除文件仍被占用、文件系统或硬件 I/O 错误。

APT 被锁或中断

1
2
3
4
ps aux | grep -E '[a]pt|[d]pkg'
systemctl status apt-daily.service apt-daily-upgrade.service
sudo dpkg --configure -a # 确认没有正常包管理进程后
sudo apt -f install # 修复依赖

不要因看到 lock 报错就直接删除 /var/lib/dpkg/lock*;活跃进程仍在使用时会损坏包数据库状态。

SSH 登录失败

1
2
3
ssh -vvv USER@HOST
getent hosts HOST
nc -vz HOST 22

服务端可查:

1
2
3
4
systemctl status ssh
journalctl -u ssh -b
ss -lntp 'sport = :22'
namei -l /home/USER/.ssh/authorized_keys

重点检查用户名、目标端口、私钥、文件权限、服务监听地址、防火墙与主机指纹变化。主机密钥告警应先核实服务器是否重装或遭受中间人攻击,不要直接删记录。

Docker 容器一启动就退出

1
2
3
4
docker ps -a
docker inspect CONTAINER --format '{{.State.Status}} {{.State.ExitCode}} {{.State.Error}}'
docker logs --tail 200 CONTAINER
docker inspect CONTAINER

容器寿命等于主进程寿命。常见原因:主命令执行完、配置/环境变量缺失、权限问题、端口冲突、依赖服务未就绪、架构不匹配、OOM。

文档与自救方法

1
2
3
4
5
6
7
8
COMMAND --help                  # 最快的参数摘要
man COMMAND # 常见命令手册,通常是第 1 节
man 5 crontab # 指定手册章节;5 常为文件格式
man 7 signal # 7 常为概念/约定
man -k KEYWORD # 按关键词搜索,等价 apropos
whatis COMMAND # 一行简介
tldr COMMAND # 社区维护的高频示例
type COMMAND # 判断 alias、函数、内建或外部程序

Shell 内建命令的帮助入口不同:

1
2
autoload -Uz run-help
run-help read # zsh:查看 read 内建帮助
1
2
help read                       # Bash:查看 read 内建帮助
help set

若 zsh 的 run-help 仍只等同于 man,检查发行版是否安装 zsh 帮助文件,并确认没有把 run-help 覆盖成简单 alias。

man 常见章节:

章节 内容
1 用户命令
2 系统调用
3 程序库函数
5 文件格式与配置
7 约定、协议、概念
8 系统管理命令

遇到陌生命令时,按这个顺序:

  1. type/command -V 确认实际运行的对象。
  2. tldr 看典型示例,--help 看参数摘要。
  3. man 阅读语法、选项、退出码、文件和示例。
  4. 在临时目录用无价值数据做最小实验。
  5. 对配置和错误信息使用精确关键词检索官方文档。

危险命令检查清单

执行以下类型命令前停一下:

  • rm -rf:目标是否为准确、非空、非根路径?变量是否已验证并加引号?
  • find ... -delete-exec rm:是否先去掉删除动作预览过匹配集合?
  • chmod -Rchown -R:是否会跨越不该修改的目录或软链接边界?
  • ddmkfs、分区工具:输入输出设备是否通过 lsblk -f 再次核对?
  • rsync --delete:是否先使用 -n dry-run,源目录末尾 / 是否符合预期?
  • docker compose down -vdocker volume rm:卷中是否有唯一数据?
  • kill -9:是否确认 PID 没复用、目标正确、普通终止无效?
  • curl | sh:脚本来源、内容、签名和执行权限是否可信?
  • sudo + 重定向:究竟哪一段获得了 root 权限?
  • Shell 通配符:展开结果是否先用 printf '%s\n' PATTERN 看过?

个人环境备忘区

将以下占位内容替换为自己的真实环境;不要记录明文密码、私钥和访问令牌。

1
2
3
4
5
6
7
8
9
10
11
12
常用发行版:
主机名 / 用途:
默认 Shell:zsh(路径通常为 `/usr/bin/zsh`)
包管理器:
主要配置目录:
常用项目目录:
备份位置与恢复方法:
SSH 别名:
常用服务及端口:
Docker Compose 项目位置:
代理 / 镜像配置位置:
遇到过的特殊问题与最终原因:

推荐保存的诊断快照

1
2
3
4
5
6
7
8
9
10
11
{
date
hostnamectl
uptime
free -h
df -hT
df -i
ip -br addr
ip route
systemctl --failed
} > "diagnostic-$(date +%F-%H%M%S).txt" 2>&1

这份快照不包含完整日志,但足以记录时间、系统、负载、内存、磁盘、网络和失败服务等第一现场。分享诊断信息前仍应检查主机名、IP、用户名和路径等隐私内容。

参考文献


  1. USTC↩︎

  2. Codex:此内容初版为 codex 生成↩︎