Linux 个人参考手册
默认环境为 Debian/Ubuntu、zsh 与 systemd;涉及 zsh 与 Bash
差异的地方同时给出 Bash 对照。未特别标注的外部 Linux
命令通常可在两者中直接使用。
约定:FILE、DIR、PID、USER、SERVICE、IMAGE
等大写词是待替换占位符;# 开头的命令通常需要 root
权限;示例中的 $、# 提示符不要输入。
快速目录
使用原则
不确定命令效果时,先查
tldr COMMAND、COMMAND --help、man 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 都能使用的命令,或实际由
find、grep、systemctl
等外部程序解析的语法。
zsh 代码块:本手册默认写法;脚本使用
#!/usr/bin/env zsh。
bash 代码块:仅用于给出有差异的 Bash 对照;脚本使用
#!/usr/bin/env bash。
交互配置默认写入 ~/.zshrc;Bash 对应
~/.bashrc。不要把一种 Shell
的专属配置直接复制到另一种配置文件。
命令风险标记
🟢
只读,通常可放心执行
ls、ps、df、grep、journalctl
🟡
会修改用户数据或运行状态
mv、chmod、systemctl restart、docker stop
🔴
可能不可恢复或影响全系统
rm -rf、mkfs、dd、kill -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]
a、b、c 中任一字符
[0-9]
一个数字字符
*.[ch]
以.c 或 .h 结尾的当前层文件
glob 由 Shell 在命令执行前展开;正则表达式由
grep、sed 等程序解释,两者不要混淆。
zsh 与 Bash 的未匹配 glob 处理不同:
1 2 3 print -rl -- ./*.log (N) setopt NULL_GLOB unsetopt NULL_GLOB
1 2 3 shopt -s nullglob 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 # 交互式查看,需安装;删除功能要谨慎
判断“磁盘满”的两条线:
df -h 的空间是否 100%。
df -i 的 inode 是否 100%;大量小文件会耗尽
inode,即使容量还有剩余。
若 du 合计明显小于 df
已用空间,检查已删除但仍被进程打开的文件:
查看、搜索与加工文本
标准流与重定向
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 读取前清空文件。使用:
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=4、w=2、x=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 GROUP。docker 组近似拥有 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 → 等待并复查 → 查日志/状态 →
必要时 KILL。D 状态进程要等内核调用返回;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+B:d
脱离、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 ApplicationAfter =network-on line.targetWants =network-on line.target[Service] Type =simpleUser =appuserGroup =appuserWorkingDirectory =/opt/myappExecStart =/opt/myapp/venv/bin/python /opt/myapp/app.pyRestart =on -failureRestartSec =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 # 服务是否监听、监听在哪个地址
判断思路:
无网卡或未 UP:检查链路、虚拟机网卡、驱动。
无 IP:检查 DHCP 或静态地址配置。
无默认路由:检查网关。
公网 IP 可通、域名不通:DNS 问题。
域名可解析、端口不通:服务、监听地址、防火墙或上游网络问题。
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
所有 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。
命令组合
A; B
无论 A 成败都执行 B
A && B
A 成功才执行 B
A | | |A
&| A 放入后台 | |{ A; B;
}| 当前 Shell 中成组执行,花括号旁要留空格 | |(A; B)`
子 Shell 中执行,不影响当前目录和变量
退出码 0 表示成功,非 0 表示失败;两种
Shell 都可用 $? 读取上一条命令退出码。zsh 还提供更易读的
$status。
管道各段退出码写法不同:
1 printf '%s\n' "${PIPESTATUS[@]} "
变量、引用与单词拆分
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 printf '<%s>\n' ${=words}
1 2 3 words='one two' printf '<%s>\n' $words printf '<%s>\n' "$words "
不要为了模仿 Bash 而在 zsh 中全局开启
SH_WORD_SPLIT;需要拆分时优先使用数组或 zsh 的
${=name} 局部标志。
zsh 将 $path 暴露为与 $PATH
自动同步的数组:
1 2 path=(~/.local/bin $path ) typeset -U path
1 2 PATH="$HOME /.local/bin:$PATH " export PATH
数组
普通数组的下标是常见陷阱:zsh 默认从 1 开始,Bash 从
0 开始。
1 2 3 4 items=(alpha beta gamma) print -r -- "$items [1]" print -rl -- "${items[@]} " items+=(delta)
1 2 3 4 items=(alpha beta gamma) printf '%s\n' "${items[0]} " 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: " printf 'Hello, %s\n' "$name " IFS= read -rs "password?Password: " printf '\n'
1 2 3 4 5 IFS= read -r -p 'Name: ' name printf 'Hello, %s\n' "$name " IFS= read -rs -p 'Password: ' password printf '\n'
两者都建议使用 -r,避免反斜杠被当作转义符。
两者的 -s
都表示静默输入,适合密码;不要保存或打印明文密码。
读入数组时 zsh 使用 read -A items,Bash 使用
read -a items。
格式化输出优先使用 printf;echo
是否解释反斜杠和选项会受 Shell 与配置影响。
逐行读取文件的下列写法在两者中通用:
1 2 3 while IFS= read -r line; do printf '%s\n' "$line" done < FILE
避免用 COMMAND | while ... 保存循环变量:zsh 默认在当前
Shell 执行管道最后一段,而 Bash 通常在子 Shell
中执行,循环后的变量结果会不同。使用上面的输入重定向,或使用
done < <(COMMAND)。
脚本参数
$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 pipefailusage () { 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 的 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 -n script.zsh zsh -x script.zsh source ~/.zshrc
1 2 3 4 5 bash script.sh bash -n script.sh bash -x script.sh source ~/.bashrc shellcheck script.sh
两者通用:
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.zsh
或 bash 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 : cleanclean: 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 可写
package、package>=1.2 或
package==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 -slimWORKDIR /app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN useradd --create-home appuser USER appuserCMD ["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:用
ldd 找
not 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-helprun-help read
若 zsh 的 run-help 仍只等同于
man,检查发行版是否安装 zsh 帮助文件,并确认没有把
run-help 覆盖成简单 alias。
man 常见章节:
1
用户命令
2
系统调用
3
程序库函数
5
文件格式与配置
7
约定、协议、概念
8
系统管理命令
遇到陌生命令时,按这个顺序:
type/command -V 确认实际运行的对象。
tldr 看典型示例,--help 看参数摘要。
man 阅读语法、选项、退出码、文件和示例。
在临时目录用无价值数据做最小实验。
对配置和错误信息使用精确关键词检索官方文档。
危险命令检查清单
执行以下类型命令前停一下:
rm -rf:目标是否为准确、非空、非根路径?变量是否已验证并加引号?
find ... -delete、-exec rm:是否先去掉删除动作预览过匹配集合?
chmod -R、chown -R:是否会跨越不该修改的目录或软链接边界?
dd、mkfs、分区工具:输入输出设备是否通过
lsblk -f 再次核对?
rsync --delete:是否先使用 -n
dry-run,源目录末尾 / 是否符合预期?
docker compose down -v、docker 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、用户名和路径等隐私内容。
参考文献