这是一个续篇。上一篇文章里我把 Linux 基础指令里最常用的目录跳转、文件创建、复制删除那些过了一遍,发文之后不少人催第二篇。我翻了一下自己各个服务器的操作历史,发现真正值得单独拿出来写一批的,其实是“每天都在用、但很少有人讲清楚原理”的那批指令。所以这一篇我打算围绕文件内容查看与搜索、权限与用户管理、压缩打包、进程与系统状态排查几条线展开,目标读者和上篇一样,既包括刚接触 Linux 的同学,也包括想把手头服务器操作从“背命令”变成“懂套路”的人。看完之后你会发现,每条指令单拆出来都不难,真正拉开效率差距的是组合方式和判断顺序。
1. 文件内容查看与检索:cat、less、tail、grep 怎么搭配才省事
1.1 cat 只是“轻量查看器”,大文件别硬扛
cat 是大多数人最先接触的看文件命令,但它真的不是万能工具。小文件比如 /etc/hosts、/etc/resolv.conf 这种几行、几十行的配置,直接 cat 一眼看全没问题。可一旦文件变成几十上百 MB 的日志或者数据文件,cat 的短板就非常明显:它的输出方式是“把整个文件往终端倒”,屏幕上会连续刷出几十屏内容,终端直接卡一下都算轻的,最要命的是你想回头找前面的内容几乎不可能,只能往上滚半天。处理这种情况,正确做法是看文件大小再决定工具,我自己的习惯有这几条:文件只有几十行,用 cat -n 带行号看;文件几百行以上,用 less;只想看头部或者尾部,用 head、tail;想按关键字过滤,直接 grep。
cat -n 显示行号这个功能经常被忽略,但排查配置文件和脚本时价值很高。比如某个 Python 服务报错“Cannot open include file: xxx”,你用 grep -n 在配置里先定位,再用 sed -n '20,30p' 把指定区间打出来,比从头到尾翻快太多。顺便提一句,cat 还有两个小参数:-b 只给非空行编号,-s 合并连续空行。最关键的一点是别把 cat 当成所有文件查看问题的统一解,它是“轻量查看器”,不是“文件阅读器”。另外提醒一下,如果手滑 cat 了一个二进制文件,比如 ELF 可执行程序或者 .so 动态库,终端里会冒出一堆乱码,严重时还会影响当前 shell 的显示,不用慌,直接执行 reset 就能恢复终端。
1.2 tail -f 和 grep 组合,是日志排查的黄金操作
日志场景里最常用的其实不是 cat,而是 tail -f。-f 表示 follow,持续输出文件里新增的内容,调试正在运行的 web 服务时几乎是标配操作。我经常像下面这样组合使用:
bash复制tail -f /var/log/nginx/access.log | grep -v "HTTP/1.1\" 200"
这条命令的意思是实时看访问日志,但把状态码为 200 的正常请求过滤掉,剩下的基本都是异常流量、错误请求或者需要关注的接口调用。如果你要同时盯多个状态码,用 grep -E "500|502|503";要排除多个无关关键字,用 grep -v -e "OPTIONS" -e "GET /favicon"。这套组合在排查 nginx 或后端服务的时候特别好用。
和 tail 相对的是 less。很多人以为 less 就是 more 的升级版,实际上两者差别很大。less 支持上下方向键逐行滚动,支持 PageUp/PageDown 翻页,支持 / 关键字向下搜索、? 向上搜索,还能按 g 跳到首行、按 G 跳到尾行。打开一个几百 MB 的日志文件,less 不会把整个文件一次性读进内存,它是按需加载的,所以操作起来非常顺滑。我习惯在 less 里加 -N 打开行号,排查配置问题时可以直接说“第 210 行有语法错误”,而不是让对方从头找。
1.3 grep 搜目录内容时,-r、-i、-l 三个开关要记牢
grep 不只是配合 tail 用,它本身就能对目录做全文检索。比如改了多个配置文件,不确定还有哪些地方引用了某个绝对路径,可以用 grep -rl "/data/app" /etc 列出所有出现该字符串的文件名。-l 只输出文件名,不会把整段匹配内容都倒出来,结果干净得多。搜索代码目录时我会再加一层过滤:grep -r --include="*.py" "def main" /opt/project/,把范围限定到 Python 文件;需要忽略大小写就加 -i,比如搜 error 时连 error、Error、ERROR 一起命中。
这里有个容易忽略的细节:grep 默认不会递归子目录,必须显式加 -r 才会往下一层层翻。如果搜索的目录里有权限不足的子目录,会冒出一堆 Permission denied 报错,这时可以把错误输出丢弃掉,写成 grep -r "keyword" /some/path 2>/dev/null,输出会干净很多。很多服务器上的代码放在 /opt 或 /srv 下,目录层级很深,合理使用 grep -r 是提升查找效率的第一步。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 权限与用户管理:chmod、chown、useradd 不只是改个数字
2.1 从 ls -l 第一列读懂权限位
ls -l 输出里的第一列,比如 -rw-r--r--,由 10 个字符组成。第一个字符表示类型:- 是普通文件,d 是目录,l 是符号链接。后面九个字符分成三组,依次对应属主、属组、其他人。每组里 r 表示读、w 表示写、x 表示执行。我带新人时经常被问到的问题之一是“为什么文件我能读,但进不了某个目录”,因为目录的 x 权限代表“能否进入这个目录”,没有它,即使文件路径看得见也进不去,更别提读取目录里的文件。
修改权限最实用的是数字法:r 记作 4,w 记作 2,x 记作 1,三者相加得到一组权限。chmod 755 的含义就是属主 7(rwx)、属组 5(r-x)、其他人 5(r-x)。常见的还有:脚本文件给 755,普通数据文件给 644,私钥这种敏感材料给 600。如果需要把所有文件统一权限,可以加 -R 递归执行,但这里有个坑:-R 会把目录和文件设置成完全一样的权限,实际场景下目录需要 x 才能进入,文件却不需要,所以更合理的做法是用 find 配合分类型处理,例如 find /data/www -type f -exec chmod 644 {} \; 和 find /data/www -type d -exec chmod 755 {} \; 分开执行。
建议:不要随手
chmod 777。777 意味着任何用户都能改写这个文件,脚本里一旦存在危险操作,风险会放大很多。先想清楚这个文件该给谁执行,再决定权限位。
2.2 新建用户为什么总在家目录和 sudo 上踩坑
Linux 新建用户时,网上很多教程直接写 useradd xxx,然后 passwd xxx。这个流程在某些发行版上确实能登录,但在另一些发行版上,用户的主目录根本不会自动创建,登录之后落点在 /,环境变量和 .bashrc 全是默认状态,很多工具用起来非常难受。原因其实很简单:useradd 是偏底层的命令,默认不负责“创建家目录”这件事,你要显式加 -m 才会生成。
我推荐一个标准流程序列:
bash复制useradd -m -s /bin/bash deploy
passwd deploy
usermod -aG wheel deploy # Debian/Ubuntu 上把 wheel 换成 sudo
-m 创建家目录,-s 指定登录 shell 为 bash。很多发行版默认 shell 可能是 sh,功能太少,日常使用应该指定为 bash。如果建用户时漏了 -m,事后也可以用 usermod -d /home/deploy -m deploy 补救。如果只是给某个应用建一个“不能登录”的系统账号,用 useradd -r 创建系统用户,配合 shell 设置为 /sbin/nologin,nginx 安装后自动创建的 nginx 用户就是这种形态。另外,用户建好之后通常还要设置密码,这个环节最容易犯的错误是密码太简单,如果服务器暴露在公网,建议直接用 passwd 设置强密码,而不是图省事留默认口令。
2.3 sudo 提权配置:visudo 是保命命令
普通用户需要安装软件、修改系统配置时,切到 root 再做虽然可行,但日志审计和管理都别扭,更推荐的方式是 sudo。用户能不能 sudo,核心看 /etc/sudoers 和 /etc/sudoers.d/ 目录。我所在的团队习惯把授权放到 /etc/sudoers.d/ 下的独立文件里,比如给 deploy 用户最小化授权,只允许重启某个服务:
bash复制deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
注意千万别直接用 vim 编辑 sudoers 文件,一定要用 visudo 命令。visudo 在退出时会做语法检查,格式写错了会拒绝保存,从机制上避免把自己锁在 sudo 门外。如果只是日常开发环境,更简单的做法是把用户加入 sudo 组或 wheel 组,组内用户输入自己的密码就可以提权。这里要特别记清楚:sudo 默认要求输入的是用户自己的密码,不是 root 的密码。把这个逻辑搞明白,权限管理的基础就算扎实了。
3. 压缩与打包:tar、zip、7z 的使用场景和隐藏细节
3.1 tar 负责归档,gzip 负责压缩,别混为一谈
用 tar 的时候,很多人习惯把 tar czvf 当成一个整体命令来背,我建议还是把“归档”和“压缩”分开理解。tar 本身只负责把一堆文件打包成独立档案,体积几乎不会有明显缩减,真正做体积压缩的是 gzip、bzip2、xz 这些算法,tar 只是把算法集成到了参数里:-z 调 gzip,-j 调 bzip2,-J 调 xz。一旦想通这个关系,遇到“为什么 tar.gz 解压出来很大但压缩包看起来不大”这类疑问就不会慌。
日常最常用的组合建议记下这四条:
bash复制tar -czvf app.tar.gz /opt/app/ # 打包并压缩
tar -xzvf app.tar.gz -C /opt/ # 解压到指定目录
tar -tzvf app.tar.gz # 只看内容列表,不解压
tar -czvf backup.tar.gz --exclude='*.log' --exclude='cache' /var/www/
-C 参数是很多新手的盲点,解压时如果不指定目标目录,文件会直接散落在当前目录里。-t 用来预览内容非常实用,不需要先把整个包解开就知道里面有哪些文件。--exclude 在备份时价值很大,比如备份 /var/www 时排除日志和缓存目录,可以省一大半空间和时间。我处理 nginx 源码包或 MySQL 二进制包时,下载到的多数是 tar.gz,一条 tar -xzvf 解开就能进目录继续下一步。
3.2 处理 7z 和 zip 之前,先确认配套工具装了没
工欲善其事,必先利其器。Linux 上默认并没有“万能解压器”,你收到一个 .zip 或 .7z 文件时,系统很可能直接提示找不到 unzip 或者 7z 命令。zip 在 Windows 和 macOS 上的兼容性很好,但服务器上没有装 unzip 就是解不了。Debian/Ubuntu 执行 apt install unzip,CentOS/RHEL 执行 yum install unzip。7z 格式压缩率高,但 Linux 上需要额外安装 p7zip-full(CentOS/RHEL 一般从 EPEL 仓库装),装好之后用 7z x archive.7z 就能解压,默认会保留目录结构,不用额外设置。
做这一步之前建议先看文件真实类型,用 file archive.zip 可以快速判断这个文件到底是什么格式。有时候扩展名可能是伪装成 .zip 或 .7z,但实际是其他编码方式,file 命令能帮你少走弯路。
3.3 备份压缩包时最容易丢的是权限和链接
对 tar 来说,默认解压已经会尽量还原文件权限,但如果打包时加了 -p,可以更完整地保留权限与时间戳,这在备份场景中很关键。系统备份恢复时,如果权限变了,服务起不来的情况很常见。zip 处理符号链接时默认不如 tar 干净,所以我的实践原则是:服务器内部备份和迁移一律用 tar,zip 只用来和 Windows 同事交换文件或者临时导出。
另外,批量处理大压缩包时,可以先列表再决定要不要全解。比如 tar -tzf xxx.tar.gz | grep 'conf$',先看看包里有哪些配置文件,再决定是否需要全部解开。这些习惯如果一开始不养成,以后面对几个 G 的备份包时会非常难受。
4. 进程与后台运行:ps、top、kill、nohup 的分工
4.1 服务器变慢时,先看 top 还是先看 ps
服务器变慢,我通常先开 top,它会把 CPU 和内存占用最高的进程实时列出来,按 P 键按 CPU 排序,按 M 键按内存排序。如果用 top -b -n 1,可以一次性输出当前状态快照,方便喂给脚本或者落盘分析。如果要在成百上千条进程里找某个关键词,ps 更合适:ps -ef 输出全部进程后配合 grep 过滤,比如 ps -ef | grep mysql;ps aux 的输出列包含 CPU 与内存占用百分比。我习惯直接执行 ps aux --sort=-%mem | head -10 查看当前吃内存最多的进程。
不过 top 里看到的进程,很多其实是 systemd 托管的。直接 kill 进程后,如果服务配置了 Restart=always,systemd 会立刻把它重新拉起来。所以判断一个进程能不能直接 kill,先看它是不是被 systemd 或 supervisor 这类守护进程拉起。是的话,正确做法是 systemctl stop 服务名 或 supervisorctl stop 服务名,而不是单纯 kill。
4.2 kill 默认是礼貌的,别总拿 -9 当万能药
不少人一遇到卡住的进程,第一反应就是 kill -9 强制杀掉,但这是彻头彻尾的最后手段。kill 默认发送 SIGTERM(15 号信号),相当于礼貌地请进程自己退出,进程收到后有机会做善后,比如释放资源、刷写日志、关闭文件描述符。kill -9 发送 SIGKILL,内核直接终止进程,不给任何清理机会。对 MySQL 这种有大量缓冲和事务日志的数据库,直接 kill -9 的下场可能是恢复阶段要花很长时间扫描数据文件,极端情况下还会丢部分提交记录。我的建议是先 kill PID 等几秒,看进程还在不在,不行再升级为 kill -9。
找 PID 的方式有很多:ps -ef | grep mysql、pgrep mysql、pidof mysqld 都行。想按命令名批量处理,可以用 pkill nginx 或 killall nginx,但这两个命令比较危险,执行前一定要确认不会误杀同名无关进程。另外还有信号 1(HUP),不少守护进程比如 nginx 可以用 kill -HUP PID 实现平滑重载配置,不中断服务,这条小知识在较长周期运行的服务器上特别实用。
4.3 nohup 和 & 组合,才是“关掉终端也能继续跑”的正解
开发时经常要在服务器上起一个脚本或服务,直接执行 ./xxx.sh 运行,一旦终端关闭,进程就会收到 SIGHUP 信号被挂断。要让它持续运行,标准组合是:
bash复制nohup ./start.sh > app.log 2>&1 &
这条命令里其实有三件事:& 把进程放到后台;nohup 让进程忽略 SIGHUP 信号,退出终端之后继续跑;> app.log 2>&1 把标准输出和标准错误都写进日志文件。如果进程已经在前台跑着,中途不想关掉它,可以先按 Ctrl+Z 暂停,再用 bg 丢到后台,之后通过 jobs -l 查看,fg %1 把它重新拉回前台。这些小操作在交互式终端里用得非常频繁。
还有一个小技巧:执行 exec -a custom_name ./script.sh 可以给脚本进程设置自定义名字,这样 ps 里看到的不再是原始脚本路径。这个方法在排查脚本进程、区分多个相似任务时很有效。注意 nohup 默认会把输出写到 nohup.out,如果你不主动重定向,这个文件会持续膨胀,最好的习惯是一开始就指定日志路径。
5. 网络和磁盘状态:ping、curl、df、du 的排障顺序
5.1 先 ping 通本机,再 curl 通业务,网络问题就拆明白了
排查网络问题,我一般按这个顺序来:先 ping 网关和本机地址,确认本机网络通不通;再 ping 一个外网 IP,比如 ping 223.5.5.5,确认能否出公网;最后 ping 一个域名,确认 DNS 解析是否正常。如果 ping 域名不通但 ping IP 通,基本可以断定 DNS 有问题;如果 ping IP 都通但业务访问异常,问题大概率出在服务本身或防火墙。
对 HTTP 服务来说,curl 比 ping 更有意义。curl -I 查看响应头,直接看到 HTTP 状态码;curl -v 输出完整握手过程,包括 TLS 证书链和重定向路径;curl -w "%{time_total}" 显示总耗时;curl -o /dev/null -s -w "%{http_code}" 用来快速确认接口是否返回 200。排查 nginx 反代故障、后端连接超时等场景,这些参数基本够用。
5.2 df 看整体,du 查局部,磁盘占满就这么定位
磁盘满的问题,几乎可以进运维日常问题 Top 3。df -h 先看整体挂载情况,哪个分区用了多少;df -Th 能额外看到文件系统类型。如果某个挂载点空间不足,再用 du -sh /目录 定位哪个子目录占了最多空间。想查目录内排名,可以执行:
bash复制du -sh /var/* 2>/dev/null | sort -rh | head -20
先把候选目录列出来,再逐层接近目标。虽然 ncdu 这类交互式工具看着更舒服,但在不额外安装依赖的前提下,du + sort 是通用性最好的组合。
我见过一个挺典型的案例:服务器磁盘每天被写满,查了 mysql 数据文件、nginx 日志都没发现问题,最后用 du -sh * 在 /home 下发现了一个被误放的 core dump 文件,几个 G 大小,清理完空间立刻释放。磁盘排查的思路一定是从大到小逐层看,不要一上来就 rm -rf 某个目录,否则删错目录的代价很大。
5.3 挂载点不只是本地磁盘,远程存储也能在 df 里看到
如果你执行 df -h 时看到类似 //nas/backup 或 /mnt/nas 这样的挂载点,说明这台机器挂载了网络共享存储,通常通过 NFS 或 CIFS 协议访问。这种挂载背后有协议配置,涉及权限、认证和网络稳定性,属于进阶内容。但至少你需要知道:df 里显示的这类挂载点并不代表本地磁盘,它的空间大小、读写速度受网络环境影响很大。排查磁盘问题时要能区分哪些是物理硬盘、哪些是远程存储,否则容易被奇怪的现象带偏。
6. 把基础指令串起来:管道、重定向和一个小巡检脚本
6.1 管道和重定向解决的是“命令间的通信”
Linux 命令最灵活的地方,就是能用管道把多个工具串成流水线。管道操作符 | 只把前一个命令的标准输出传给后一个命令的标准输入,标准错误不会进管道。所以当看到 ps aux | grep nginx 输出为空但明明有 nginx 进程时,就要考虑目标进程是否属于别的用户或命名空间,导致当前用户看不到。
重定向是另一个基础能力:> 覆盖写文件,>> 追加写文件,2>&1 把标准错误并进标准输出。把不关心的输出丢到 /dev/null 也是常见操作。日常写巡检脚本时,我常这样控制输出方向:cmd >> /var/log/cron.log 2>&1,成功时保留历史,出错时错误信息也能落盘;调试时就改用 cmd 2>/dev/null,只留有用的内容。
6.2 一个巡检脚本,把前面大部分指令用起来
为了把这些基础指令串起来,我提供一个很短但实用的巡检脚本,它把磁盘、内存、进程、端口、日志串成一条命令:
bash复制#!/bin/bash
echo "===== 磁盘使用 ====="
df -h | awk 'NR==1 || $5+0 > 80 {print}'
echo "===== 内存 TOP 进程 ====="
ps aux --sort=-%mem | head -6
echo "===== 当前监听端口 ====="
ss -tlnp | head -10
echo "===== 最近 10 条登录日志 ====="
tail -n 10 /var/log/auth.log 2>/dev/null || journalctl -n 10 2>/dev/null
第一段用 df -h 筛选使用率超过 80% 的分区,快速抓住磁盘告警;第二段把内存占用最高的进程放最前面,方便判断是否有进程失控;第三段用 ss -tlnp 查看端口监听状态,确认关键服务都在正常监听;第四段看登录与提权日志,及时发现异常行为。这里的 awk 不是必须掌握的,但至少能看出“每个脚本行都在帮你回答一个具体问题”。
6.3 写脚本最容易栽的跟头,集中在三处
最后说三个写 shell 脚本特别容易踩的坑。第一,脚本第一行一定要写 #!/bin/bash,因为不同发行版默认 shell 不一样,不写解释器,脚本行为可能不一致。第二,脚本里有多个步骤的,加上 set -e,让一行命令失败就停止整个脚本,避免在错误状态下继续往下跑。第三,引用变量时一定加引号,比如 [ -f "$file" ],不加引号,文件名带空格时判断会出错。这些细节在基础指令里不会直接教,但实际写脚本很快会遇到。
我在实际运维里见过太多把 kill -9 当万能招数的人,也见过上来就 rm -rf 却忘了先 df -h 看磁盘占用的情况。其实把基础指令吃透,再养成“先查状态、再定位、最后动手”的习惯,绝大多数服务器的日常问题都能在几分钟内找到头绪。包括这一篇里讲的日志检索、权限配置、压缩解包、进程管理和网络排查,每一条单独看都很朴素,但组合起来就是一套能落地的排障流程。如果你是刚接触 Linux 的读者,建议照着敲一遍;如果已经用过一段时间,可以试着把第 6 节那个巡检脚本扩展成自己的版本,它比单纯背命令参数更能提升实战感。
