四大重点:环境变量与配置文件、软链接与硬链接、文本处理三剑客(grep/sed/awk)、文件查找与归档(find/tar)。 另附其余中级知识点速查表(用户与组、定时任务、权限、日志、进程、网络、包管理、重定向)与踩坑清单。
一、环境变量与配置文件
是什么
环境变量是"贴在 shell 身上的便签",进程启动时继承一份副本。PATH 就是最典型的一个------它决定你敲 ls 时系统去哪些目录找这个程序。
env # 查看所有环境变量
printenv PATH # 只看某一个
echo $PATH # 同上
临时 vs 永久
export PATH=$PATH:/opt/bin # 仅在当前 shell + 它的子进程有效,关掉终端就没了
永久生效必须写进配置文件:
| 文件 | 生效范围 | 被谁加载 |
|---|---|---|
~/.bashrc |
当前用户 | 交互式非登录 shell(新开终端标签页、tmux 窗口) |
~/.bash_profile / ~/.profile |
当前用户 | 交互式登录 shell(SSH 登录、tty 登录) |
/etc/profile |
所有用户 | 登录 shell(全局版 profile) |
/etc/environment |
所有用户 | 由 PAM 加载,只能写 KEY=value,不支持 shell 语法 |
source ~/.bashrc # 改完立即生效,不用重开终端
实际中
~/.bash_profile里通常有一行source ~/.bashrc,所以两边内容经常同步。记不住谁是登录 shell 也没关系------个人配置写~/.bashrc、全局变量写/etc/environment,能覆盖 95% 场景。
三个坑
export是"继承开关" :不写 export 的变量,子进程看不到(bash -c 'echo $FOO'输出空)- PATH 追加别丢了原值 :
export PATH=/opt/bin会把系统路径全冲掉,必须写PATH=$PATH:/opt/bin sudo会重置 PATH :sudo默认走secure_path,你加的路径可能不生效------用绝对路径调用,或改visudo里的secure_path
二、软链接与硬链接
一句话本质
-
硬链接 :同一个 inode 的另一个名字(一本内容,两个门牌号)
-
软链接(符号链接) :一个小文件,里面存的是目标的路径(一张写着"去隔壁 302"的纸条)
ln /opt/app/v1.2/app.bin /opt/app/app-hard # 硬链接
ln -s /opt/app/v1.2 /var/www/app # 软链接(生产常用)
对比表
| 维度 | 硬链接 | 软链接 |
|---|---|---|
| 存的是什么 | inode 编号(同一份数据) | 目标路径字符串 |
| 删除原文件 | 数据仍在(链接数减 1) | 悬挂失效(红闪) |
| 跨文件系统 | ❌ 不行 | ✅ 可以 |
| 链接目录 | ❌ 不行 | ✅ 可以 |
ls -l 显示 |
普通文件 - |
l 开头 + -> 箭头 |
| 链接数 | 会增加 | 不影响 |
为什么硬链接不能跨文件系统 :inode 编号只在各自文件系统内唯一,跨分区的编号指向无意义。
为什么硬链接不能链目录 :目录硬链接会产生循环引用(a/.. 指回 a),文件系统遍历时无限递归。
常用命令
ls -l /var/www/app # 第二列数字 = 链接数;软链接可见 -> 箭头
stat app.bin # 看 inode、链接数
readlink -f /var/www/app # 解析软链接最终指向的真实路径
ln -sfn /opt/app/v1.3 /var/www/app # 原子切换 + -n 防套娃,发布/回滚标准动作
生产价值
版本发布用软链接做"开关":/var/www/app -> /opt/app/v1.3。回滚只需要把链接指向 v1.2------不用改任何配置、不用重启(应用需支持重新加载路径)。
三、文本处理三剑客(grep / sed / awk)
| 工具 | 定位 | 一句话 |
|---|---|---|
grep |
找行 | 按内容过滤,保留匹配的行 |
sed |
改文本 | 按规则替换、删除、插入 |
awk |
处理列 | 把行拆成字段,做统计和计算 |
grep
grep ' 500 ' /var/log/nginx/access.log # 状态码两侧带空格,避免误匹配 IP 里的 500
grep -i error app.log # 忽略大小写
grep -v '^#' /etc/nginx/nginx.conf # 反选:排除注释行
grep -c 'ERROR' app.log # 只输出命中行数
grep -rn 'timeout' /etc/nginx/ # 递归目录 + 显示行号
grep -E 'error|fail|timeout' app.log # 扩展正则,多关键词
sed
sed 's/192.168.1.10/192.168.1.254/g' access.log # 全局替换,只输出到屏幕
sed -i.bak 's/old/new/g' config.conf # 原地修改并留 .bak 备份
sed -n '1,20p' access.log # 只打印 1-20 行(-n 抑制默认输出)
sed '/^$/d' file.txt # 删除空行
sed 's/^/ /' file.txt # 每行行首加缩进
不加
g时每行只替换第一处------这是最高频的 sed 事故。
awk
awk '{print $1}' access.log # 取第 1 列
awk -F: '{print $1, $7}' /etc/passwd # 指定分隔符为冒号
awk '{print NR": "$0}' file # NR = 行号
awk '$9 == 500 {print $1, $7}' access.log # 条件筛选:状态码 500 的 IP + 路径
awk '{sum += $10} END {print sum}' access.log # 汇总流量
经典组合(摸底与面试高频)------统计每个 IP 的访问次数,降序:
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
数据流方向 :awk 取列 → sort 排序 → uniq -c 计数 → sort -rn 降序。记住 uniq 只能统计相邻 重复行,所以第一次 sort 不能省。
四、文件查找与归档(find / tar)
find:按"属性"找文件
find /var/log -name "*.log" # 按名字(记得加引号防 shell 展开)
find /var/log -name "*.log" -mtime +7 # 修改时间超过 7 天
find /var/log -mtime -1 # 1 天内改动过
find /etc -user root -perm 644 # 按属主 + 权限(精确匹配)
find / -size +100M -type f # 大于 100M 的普通文件
find /tmp -type f -name "*.tmp" -delete # 找到即删
find /backup -type f -mtime +30 -exec rm -f {} \; # 逐个交给 rm 处理
find /data -type f -mtime +30 -print0 | xargs -0 rm -f # 文件名含空格更安全
-mtime +7:+ 表示"超过",-表示"以内",7是 7×24 小时前-exec ... {} \;中{}是占位符,\;是结束符(分号要转义)xargs与-exec的区别:-exec逐个调用命令,xargs批量拼接参数,文件多时更快
tar:打包 + 压缩
tar -czvf /tmp/logs.tar.gz /var/log # 打包压缩
tar -xzvf /tmp/logs.tar.gz -C /tmp/extract # 解压到指定目录
tar -tzvf /tmp/logs.tar.gz # 只看内容列表,不解压
tar -xzvf logs.tar.gz etc/nginx/nginx.conf # 只解出指定文件
| 参数 | 含义 |
|---|---|
-c |
create 创建归档 |
-x |
extract 解压 |
-t |
list 列出内容 |
-z |
gzip 压缩(.tar.gz);-j 是 bzip2,-J 是 xz |
-v |
显示过程 |
-f |
指定归档文件名(必须紧跟文件名) |
-C |
切换到目标目录(解压时指定位置) |
⚠️ 两条铁律:
- 顺序是"归档名在前、源在后" :
tar -czvf 出来的包 要打包的目录------写反了会把源目录名当包名 - 解压到指定目录必须用
-C:直接写目录名会被当成"要解出的文件",静默失败
zip / unzip(与 Windows 交换文件时用)
zip -r backup.zip /var/log # 递归压缩
unzip -l backup.zip # 列内容
unzip backup.zip -d /tmp/out # 解压到目录
五、其余知识点速查
用户与组管理
useradd -m -s /bin/bash alice # -m 建家目录,-s 指定 shell(必须写路径)
passwd alice # 设置密码
usermod -aG wheel alice # 加入 sudo 组(-a 必须有,否则踢出其他附加组)
groupadd devteam && usermod -aG devteam alice
id alice # 查看所属组
命令职责别混:chmod 改文件权限、chown 改文件属主、usermod 改用户属性。
定时任务
crontab -e # 编辑(有语法校验),不要直接 vim 编辑 cron 文件
crontab -l # 查看
30 2 * * * /opt/backup.sh # 每天 02:30(分 时 日 月 周,用空格分隔)
*/5 * * * * /opt/check.sh # 每 5 分钟(*/5 = 每 5 个;写 5 = 每小时的第 5 分钟)
0 3 * * 0 /opt/weekly.sh # 每周日 03:00
生产环境注意:cron 的 PATH 很简陋,脚本里命令写绝对路径 、开头
cd到工作目录,否则容易"手动能跑、定时失败"。
文件权限
chmod 755 script.sh # rwxr-xr-x:所有人可执行,只有属主可写
chmod 644 config.conf # rw-r--r--:只有属主可写
chmod u+x,g-w file # 符号法:属主加执行、组去掉写
chown deploy:devteam file # 改属主和属组
chattr +i /etc/hosts # 加不可变属性,连 root 也不能改/删(-i 解锁,lsattr 查看)
umask 022 # 决定新建文件的默认权限(777 - umask)
日志查看
journalctl -u nginx --since today --tail 100 # 指定服务 + 今天 + 最后 100 行
journalctl -u nginx -f # 实时跟踪
journalctl -u nginx --since "10 min ago" -p err # 只看错误级别
tail -n 50 app.log # 最后 50 行(tail 用 -n,--tail 是 journalctl 的参数)
tail -f app.log # 实时跟踪
less big.log # G 到末尾,g 回开头,/关键词 搜索,n 下一个,q 退出
进程管理
ps aux --sort=-%cpu | head -5 # CPU 占用前 5
top -o %CPU # 实时排序
kill 3456 # 默认 SIGTERM,优雅终止
kill -9 3456 # SIGKILL 强杀(最后手段,可能损坏数据/端口不释放)
systemctl status nginx # 服务状态
systemctl restart nginx # 重启
网络排查
ip addr # 查网卡和 IP(ifconfig 属 net-tools,新系统推荐 ip)
ss -tlnp | grep 8080 # 查端口监听 + 进程(ps aux | grep 8080 查不到端口!)
curl -I https://example.com # 只看响应头,排障更快
curl -o /dev/null -s -w "%{http_code} %{time_total}\n" https://example.com # 只取状态码和耗时
ping -c 4 host && traceroute host # 连通性 + 路径
包管理
apt update && apt install -y nginx # Debian/Ubuntu
dpkg -l | grep nginx # 查包是否安装(nginx -v 查的是程序版本,不是包)
yum / dnf install -y nginx # CentOS/RHEL(dnf 是 RHEL8+ 的新一代)
./configure && make && make install # 源码三步曲:检测环境 → 编译 → 安装
重定向与管道
command > out.log # 覆盖
command >> out.log # 追加
command > out.log 2>&1 # 错误和正常输出都进文件(先定目标文件,再合并错误流)
command > /dev/null 2>&1 # 全部丢弃(/dev/null 是黑洞设备)
ps aux | grep nginx # 管道:前一条的 stdout → 后一条的 stdin
echo $? # 上一条命令的退出码(必须紧跟,中间插别的命令就失效)
六、踩坑速查表
| 场景 | 正确写法 | 错误写法 / 后果 |
|---|---|---|
输出含 $ 的字面量 |
echo '$PATH' |
双引号会展开变量 |
| 变量后紧跟字母 | "${svc}.service" |
$svc.service 边界歧义 |
| find 按名字 | -name "*.log" |
不加引号被 shell 提前展开 |
| tar 顺序 | tar -czvf 包.tar.gz 源目录 |
写反把源目录当包名 |
| tar 解压到目录 | 加 -C /tmp/out |
直接写目录名被当成文件名 |
| 统计 IP 次数 | `sort | uniq -c` |
| 数值比较 | -gt -lt -eq |
> < 在 [ ] 里是重定向 |
| 命令当条件 | if 命令; then |
if [ 命令 ]; then(test 报错,被 2>&1 吞掉 → 静默全反) |
| 查端口监听 | `ss -tlnp | grep 8080` |
| 幂等切换软链接 | ln -sfn 新目录 链接 |
少了 -n 会在链接里套链接 |
| 引号 | 半角 " |
全角 ""(bash 不认,语法高亮失效) |
七、记什么、查什么
| 级别 | 内容 |
|---|---|
| 内化(忘不了的那种) | 硬链接=同一 inode 的另一个名字、软链接存路径;grep 找行 / sed 改文 / awk 挑列;tar 归档名在前;-C 指定解压目录;find -mtime +N 的 + 是"超过" |
| 翻文档即可 | 三剑客的参数全集、find 的几十个测试条件、tar 的压缩算法开关 ------ man grep / tldr tar / --help 随手可查 |
命令是查出来的,模型是想明白的。搞反
scp方向、忘写tar -C、sed忘了g的人,不是记性差,是模型没建起来。