☰
Linux学习笔记 day02:系统管理与运维排障实战指南
2026/10/8 9:42:11 网站建设 项目流程

1. 学习笔记 day02:从能敲命令到会干活的进阶之路

昨天我们把 Linux 环境跑起来,学会了怎么进出目录、怎么看文件,算是正式踏进了这个黑乎乎的命令行世界。今天这篇 day02 笔记,我会延续第一天的节奏,专门梳理那些日常使用频率最高、面试也常被问到的命令和概念,包括用户与权限、进程管理、文本处理、系统状态查看、网络排查,最后再搭配几个我实际工作中踩过的坑和故障案例。学完今天这些内容,你就不再是只会敲ls的小白了,至少能处理一些真实的运维场景,看得懂系统日志,也敢动手去排查问题了。

这篇笔记适合刚学完基础命令、想往运维或嵌入式方向走的同学,也适合那些命令行用了很久但一直靠死记硬背、没搞清楚底层逻辑的朋友。我尽量不堆砌命令列表,而是把每个命令放到实际场景里讲,告诉你为什么用、什么时候用、用了之后看什么。毕竟 Linux 这东西,光背命令是记不住的,你得理解它背后的设计思路——一切皆文件、权限驱动、进程协作,顺着这条线往下走,命令自然就串起来了。

先说一下今天笔记的定位:day02 不碰编译内核、不搞驱动开发,也不深入源码,专注把“日常管理和排障”这层打扎实。具体内容包括用户与文件权限、进程与后台任务、文本与日志处理、系统资源查看、网络工具,以及几个我自己遇到过的真实故障案例。每段我都会给出命令示例和执行思路,你看完可以直接在自己的虚拟机里练一遍。

2. 用户与权限:Linux 安全模型的基石

2.1 用户、组、权限位到底怎么配合

很多新手刚接触 Linux 权限时,最困惑的一件事就是:明明文件是自己的,为什么改了没效果?为什么别人能读我不能读?这背后其实就是 Linux 最核心的安全模型——用户(User)、组(Group)、其他(Other)三级权限体系。

在 Linux 里,每个文件都记着三组权限:属主(owner)权限、属组(group)权限、其他用户(others)权限。每组权限又分读(r=4)、写(w=2)、执行(x=1)三种,数字表示法是它们的和。比如755表示属主可读写执行(7=4+2+1),属组可读可执行(5=4+1),其他人可读可执行(5=4+1)。这套设计相比 Windows 的 ACL 权限要简单直接得多,但同时它也意味着你必须理解文件属主和进程运行用户的关系,否则容易出现权限混乱。

我举个真实场景:在服务器上部署一个 Web 应用,Nginx 进程以www-data用户运行,站点目录属主如果被设成root:root,权限是755,那 Web 进程能读文件但不能写。如果应用需要上传文件,你只改文件权限改成777,当时能写,但这是非常危险的做法,因为任何本地用户都能改了。正确做法是:把目录属主改成www-data,或者把www-data加进目录属组,然后给属组加写权限。

这里我建议你养成一个习惯:拿到任何一个 Linux 系统,第一件事就是用id命令看当前用户和组信息,用ls -l看关键目录的属主属组。理解了这套权限模型,后面配 SSH、配 Web、搞共享目录都会顺很多。

2.2 用户管理常用操作:useradd、usermod、passwd

创建用户是 day02 必须练熟的操作。推荐用useradd加参数的方式,而不是直接改/etc/passwd,虽然直接改文件在某些紧急场景下也能用,但容易出错。

# 创建用户,同时指定 home 目录、登录 shell、附加组 useradd -m -d /home/zhangsan -s /bin/bash -G wheel zhangsan # -m:自动创建 home 目录 # -d:指定 home 目录路径 # -s:指定登录 shell # -G:附加组,可以多个,逗号分隔 # 设置或修改密码 passwd zhangsan # 查看用户信息 id zhangsan

这里有个细节:如果你不加-m,很多发行版默认不会自动创建 home 目录,用户登录后进到一个不存在的目录,会出现各种路径问题。另外-G wheel是为了让用户加入管理员组,具体组名看发行版,CentOS/RHEL 系是wheel,Ubuntu/Debian 系是sudo。

删除用户用userdel -r zhangsan,-r会连 home 目录和邮件池一起删,不加-r则只删用户但保留文件。你要是有强迫症,删完用户记得检查一下/etc/passwd、/etc/group、/etc/shadow三个文件里有没有残留条目,避免 uid 复用导致权限串号。

2.3 chmod、chown、chgrp 的实操细节

权限修改三兄弟:chmod改权限位,chown改属主,chgrp改属组。这仨命令看起来简单,但实际使用中有几个坑我踩过好几次。

第一个坑:chown后面是先写属主再写属组,中间用冒号分隔,比如chown root:root file。很多新手容易把顺序搞反,写成chown root:root还好,如果要只改属组,得写chown :group file,注意前面的冒号不能省,省了就会把group当成用户名去解析,直接报错。

第二个坑:递归授权要谨慎。chmod -R 777 /some/dir这种命令,我强烈建议少用。一旦你递归改了系统目录,比如/usr或/var,那系统基本就半残了——某些程序启动时会因为关键文件权限过宽而拒绝运行,更糟的是给恶意程序开了后门。正确做法是分级授权,比如网站目录下,目录用755,普通文件用644,需要写入的上传目录单独设775属组可写。

第三个坑:chmod除了数字法,还有符号法。比如chmod u+x file表示给属主加执行权限,chmod g-w,o-r file表示属组去写、其他去读。符号法在微调权限时比数字法直观,但很多教程不讲,导致新手只会777。实际排障时,符号法非常有用,比如一键给所有 shell 脚本加执行权限:

find /path/to/scripts -name "*.sh" -exec chmod +x {} \;

2.4 用 ACL 补足基础权限的短板

基础权限模型的短板在于:一个文件只能有一个属主和一个属组。但实际场景里经常出现“A、B 两个不同组的人都要能读写某个共享目录”的需求,这时候基础权限解决不了,得用 ACL(Access Control List)。

ACL 可以给特定用户或组单独设置权限,而不受传统属主/属组限制。查看 ACL 用getfacl,设置用setfacl:

# 给 lisi 用户针对 /data/share 目录增加读写执行权限 setfacl -m u:lisi:rwx /data/share # 删除某个用户的 ACL 条目 setfacl -x u:lisi /data/share # 递归设置目录及目录下已有文件的 ACL setfacl -Rm u:lisi:rwx /data/share

注意:递归-R只对已有文件生效,如果以后在目录里新建了文件,默认不会继承 ACL。要让新文件自动继承,需要设置默认 ACL:

setfacl -m d:u:lisi:rwx /data/share

这个d:前缀就是 default 的意思,设置之后,这个目录下新建的文件会自动赋予 lisi 读写执行权限。这个技巧在做团队共享目录、FTP 目录、Samba 共享时非常实用。

3. 进程管理与后台任务:别再用 Ctrl+C 硬扛所有进程

3.1 ps、top、htop 的合适使用场景

进程管理是运维的核心功,不会看进程状态,排查问题几乎寸步难行。

ps是静态快照,适合“看一眼当前有哪些进程”。最常用的组合是ps -ef和ps aux,两者展示的信息略有差别,但基本都包含 PID(进程号)、PPID(父进程号)、CPU%、内存%、启动命令。如果你想找某个特定进程,配合grep是最快的方式:

ps -ef | grep nginx

这里有个小技巧:用grep -v grep把 grep 自身那行过滤掉,或者直接用pgrep -af nginx,pgrep 命令就是专门干这个的,更干净。pgrep -f是按完整命令行匹配,而不是只匹配进程名,在找脚本进程时特别管用。

top是动态刷新,适合“持续观察哪几个进程吃 CPU、吃内存”。进去之后按P按 CPU 排序,按M按内存排序,按q退出。如果你在排查内存泄漏,top 的RES列(常驻内存)比VIRT列更有参考价值,VIRT 只是虚拟内存,不代表真实占用。

htop是 top 的增强版,支持鼠标操作、树状视图、直接 F9 杀进程,但很多最小化安装的服务器没有,需要自己装。我的建议是:top 必须熟练,htop 能装就装,毕竟排障时一个高亮彩色界面比满屏黑字舒服得多。

3.2 后台运行:&、nohup、setsid、tmux 一次讲清

新手最常见的困惑是:明明程序在终端里跑得好好的,一关终端窗口程序就死了,这是为什么?

因为大多数程序是终端的前台进程,终端关闭时内核会向会话中的进程发送 SIGHUP 信号,进程没处理这个信号,默认行为就是终止。解决办法就是让进程脱离终端会话。

最简单的是在命令末尾加&,让它在后台运行。但这只解决了“不占你终端”的问题,终端关闭时它依然可能被杀,因为它的会话还是和终端关联。

更可靠的是nohup:

nohup python3 myscript.py > /var/log/myscript.log 2>&1 &

拆解一下:nohup让进程忽略 SIGHUP,> logfile把标准输出重定向到文件,2>&1把标准错误也并到标准输出里,最后的&让命令在后台运行。这个命令组合是运维日常最常用的启动姿势,没有之一。

setsid更彻底,它让进程直接成为新的会话首进程,完全脱离终端控制。用法更简单:

setsid python3 myscript.py > myscript.log 2>&1

连&都不用加,因为它已经是独立的会话了。但说实话,日常场景里nohup已经够用,setsid我一般只在写服务启动脚本时用。

如果你需要更高级的会话管理,比如关掉终端后过几小时再回来查看程序输出、在远程跑长任务、同事之间共享一个操作会话,那就该上tmux了。tmux 是终端复用器的王者,最核心的概念是“会话(session)”和“窗口(window)”:

tmux new -s work # 新建一个名为 work 的会话 tmux detach # 从会话中分离(相当于挂起),快捷键 Ctrl+b d tmux ls # 查看所有会话 tmux attach -t work # 重新连接 work 会话

在 tmux 里跑任务,就算你 SSH 断开,任务照常运行。回来tmux attach就能看到现场,这一点在跑长任务、部署脚本、数据库迁移时真的救命。

3.3 kill、killall、pkill:杀进程的高阶姿势

杀进程不是上来就kill -9。很多新手的习惯是:卡住了就kill -9,结果不仅没解决问题,还可能把数据弄丢。这里必须理解 Linux 信号机制。

kill默认发送 SIGTERM(15),这是“请优雅退出”,程序收到后会清理资源、保存状态、然后退出。kill -9发送 SIGKILL,这是“立刻终止,不容商量”,内核直接回收资源,但程序没机会做任何清理,可能造成文件损坏、数据不一致。

正确流程是:先kill -15 PID,等几秒看进程是否还在,不行再kill -9 PID。当然,对于僵尸进程(Z 状态),kill 是无效的,你只能杀它的父进程让 init 去回收。

killall nginx按进程名杀,pkill -f "python.*server.py"按完整命令行匹配杀,这两者比逐个查 PID 要快得多。但有个大坑:pkill -f太容易被自己误伤——比如你用pkill -f "grep"可能会把pgrep或者pkill自己匹配进去。更安全的做法是,先用pgrep -af看一下匹配到了哪些进程,确认没问题再杀。

3.4 系统服务管理:systemctl 的日常用法

现在的主流发行版都用 systemd 管理服务,service和chkconfig在老系统上还能见到,但新环境基本是systemctl的天下。day02 至少掌握这几个:

systemctl start nginx # 启动服务 systemctl stop nginx # 停止服务 systemctl restart nginx # 重启服务 systemctl status nginx # 查看服务状态,包括 PID、最近日志、活跃状态 systemctl enable nginx # 设置开机自启 systemctl disable nginx # 取消开机自启 systemctl list-units --type=service --state=running # 列出所有运行中的服务

systemctl status输出的信息量很大,包括服务当前状态(active/exited/failed)、主进程 PID、最近几条日志。排障时先跑这个,基本能确定服务是被杀掉了、启动失败还是压根没启动。

还有个隐藏技巧:systemctl status是有退出码的。进程正常运行返回 0,异常返回非 0,在脚本里可以用$?判断服务是否健康。比如:

systemctl status nginx > /dev/null 2>&1 if [ $? -ne 0 ]; then systemctl restart nginx fi

这就在 shell 脚本里实现了一个极简的保活逻辑。

4. 文本处理三剑客:grep、sed、awk 的实战组合

4.1 grep:不只是简单的关键词搜索

grep 是 Linux 上使用频率最高的命令之一,但很多人只会grep keyword file这种简单用法。稍微深入一点,grep 能做的事情远超你想象。

首先,grep -r可以递归搜索目录,排错日志时特别常用。grep -i忽略大小写,grep -n显示行号,grep -v反选(排除匹配行)。组合起来就是:

grep -rn "ERROR" /var/log/app/ | grep -v "healthcheck"

其次,grep -A和grep -B可以在匹配行之后/之前显示指定行数的上下文,看异常日志时非常实用。比如查看报错前后各 5 行:

grep -A 5 -B 5 "OutOfMemoryError" /var/log/app.log

还有grep -E支持扩展正则,grep -P支持 Perl 兼容正则。比如查找所有 IP 地址,一条命令搞定:

grep -P -o "\d{1,3}(\.\d{1,3}){3}" access.log | sort | uniq -c | sort -rn

这条命令还能顺带统计每个 IP 出现的次数,排在前面的就是访问最频繁的,用来识别异常扫描很有用。

4.2 sed:流编辑器,增删改查一把梭

sed 的核心用法是“流式处理”,它逐行读取文件,对每一行执行替换、删除、插入操作,默认不修改原始文件,除非加-i。

最常用的替换操作:

# 把文件里所有 old 替换成 new,预览到屏幕(不写文件) sed 's/old/new/g' file.txt # 直接修改文件(注意 -i 会覆盖原文件,建议先备份) sed -i 's/old/new/g' file.txt # 只替换每行第 2 次出现的内容 sed 's/old/new/2' file.txt

-i后面可以跟备份后缀,比如sed -i.bak 's/old/new/g' file.txt,会自动生成一个file.txt.bak备份文件再改原文件。改系统配置前我强烈建议养成这个习惯,改坏了还能秒回滚。

sed 也可以按行号操作:

sed -n '5,10p' file.txt # 查看第 5 到 10 行 sed '/ERROR/d' file.txt # 删除所有包含 ERROR 的行

-n和p的组合就是“只打印匹配的行”,相当于一个更精确的 grep。把所有 Nginx 配置里被注释掉的 server_name 行摘出来,只用一个命令:

sed -n 's/^#\s*server_name\s*//p' nginx.conf

4.3 awk:按列处理的王牌工具

awk 是按列处理文本的神器,尤其适合处理日志、表格数据。它的核心思想是:把每一行按分隔符拆成多个字段,然后对字段做处理。默认分隔符是空格,可以用-F指定。

最经典的用法是打印指定列:

# 打印 df 输出中的第 1 列(文件系统)和第 5 列(使用率) df -h | awk '{print $1, $5}'

再进阶一点,awk 可以做条件判断和统计:

# 找出磁盘使用率超过 80% 的分区 df -h | awk 'NR>1 && +$5 > 80 {print $1, $5}' # 统计日志中 500 错误的数量 awk '$9 == 500 {count++} END {print count}' access.log

awk 最大的价值在于它本身就是一门小语言,有变量、循环、数组。比如统计访问量 Top 10 的 URL:

awk '{count[$7]++} END {for (url in count) print count[url], url}' access.log | sort -rn | head -10

这条命令的意思:对每一行,用第 7 列(URL)做 key,给对应的计数器加 1。文件处理完后,遍历所有 URL 打印计数,再用sort -rn倒序排序取前 10。这放在任何日志分析工具里都是核心逻辑。

4.4 组合实战:从日志里快速定位故障现场

三个工具单独用都简单,组合起来才见功力。举个例子:假设你是 Java 应用运维,应用日志打印格式是2025-01-15 10:22:33 ERROR - NullPointerException,你要统计当天每个小时出现 ERROR 的次数,然后找出次数最多的那一个小时出现在哪些行。

第一步,用 awk 提取小时字段并按小时分组统计:

awk '{hour=substr($2,1,2); if ($3=="ERROR") count[hour]++} END {for (h in count) print h, count[h]}' app.log | sort -k2 -rn

第二步,找到最多的小时,假设是14,再筛出那一小时内所有 ERROR 的完整日志:

grep " 14:" app.log | grep "ERROR"

第三步,如果信息还不够,用 sed 把错误前后的上下文捞出来:

grep -n "NullPointerException" app.log | head -5 | awk -F: '{print $1}' | xargs -I{} sed -n '{},{}p' app.log

这三板斧打完,问题基本就能定位到具体代码行或者具体输入参数了。这套组合拳在没有任何日志分析平台的裸环境下,就是我处理问题的主要手段。

5. 系统状态与资源查看:知己知彼,百战不殆

5.1 df、du 与磁盘占用排查

磁盘满是最常见的故障之一,也是最容易排查的。df -h看文件系统整体使用率,du -sh看目录总共多大,du -h --max-depth=1看一层目录的大小分布。

排查“/ 分区红了但不知道什么占的空间”的标准流程:

df -h # / 分区 100% 满 cd / du -h --max-depth=1 | sort -rh | head -10 # 找到 /var 占最大 cd /var du -h --max-depth=1 | sort -rh | head -10 # 再往下钻到 /var/log,找到几个巨型日志文件

这个过程就像剥洋葱,一层层找到最大的目录,最后一层定位到大文件。我经常遇到的情况是:某个进程持续输出日志,日志文件不断增长,几分钟就把磁盘写满。这时候除了删日志,更重要的是搞清楚为什么日志会爆发式增长——多半是应用进入了死循环或者报错刷屏。

还有个隐藏技巧:用lsof | grep deleted找出“已被删除但仍有进程占用”的文件。这种文件不占目录空间,但占磁盘空间。如果删了文件磁盘空间没释放,大概率就是这个原因。比如网站上传的临时图片被删除后,如果 Nginx 进程还在写这个句柄,文件占的空间就一直在。用lsof找到 PID 再重启那个进程,空间才会真正释放。

5.2 free、vmstat、iostat 读懂内存与 IO

free -h看内存使用。很多新手看到free里 avail 值小就急着加内存,其实这是误区。Linux 的内存策略是“能用就用”——内存空闲着也是浪费,不如拿去缓存磁盘块。所以看内存压力不能只看 free 列,要看available列(真实可用的内存),以及swap的使用情况。

如果 swap 持续增长,说明物理内存真的不够了。排查到底谁在吃内存,用top按内存排序,或者用ps快照:

ps aux --sort=-%mem | head -10

vmstat更适合看整体趋势。vmstat 1每秒刷新一次,重点关注r(运行队列)、b(阻塞进程数)、si/so(换入换出)这几列。如果r长期大于 CPU 核数,说明 CPU 过载;如果si/so常年不为 0,说明内存压力很大,系统正在频繁换页,这时候加内存比调优来得更实在。

iostat看磁盘 IO,重点看%util和await。%util接近 100% 说明磁盘已成为瓶颈,await过长说明 IO 响应慢。遇到数据库慢查询排查时,iostat 能帮你判断是 SQL 的问题还是磁盘硬件的问题。

5.3 系统时间与时区管理

时间同步是运维里一个看似简单、坑却很多的点。如果服务器时间和真实时间差了太多,会导致日志时间错乱、HTTPS 证书校验失败、数据库主从同步异常等一系列问题。

查看时间:

date # 系统时间 date -R # 带时区显示,比如 +0800 timedatectl # 查看系统时间、时区、NTP 同步状态

修改时区:

timedatectl set-timezone Asia/Shanghai

NTP 同步:

# 手动同步一次 ntpdate -u ntp.aliyun.com # 老工具,很多新系统已移除 # 推荐用 chrony systemctl restart chronyd chronyc sources -v # 查看 NTP 同步状态

如果发现服务器时间一直不准,优先检查 chronyd 或 ntpd 是否运行、是否能连通 NTP 服务器。防火墙挡了 UDP 123 端口是一个常见原因。

5.4 网络查看与连接排查

网络排查在 day02 先掌握最基础的工具组合。ip addr看网卡 IP,ip route看路由,ping看连通性。如果要监听的端口被占了,用ss或旧的netstat:

ss -tlnp # 查看所有监听的 TCP 端口及对应进程 ss -antp # 查看所有 TCP 连接状态

排查“端口起不来”的标准姿势:

ss -tlnp | grep :8080 # 8080 是否已被别的进程占用

如果是服务器的端口明明通了但外部访问不了,那大概率是防火墙问题。用firewall-cmd --list-all或ufw status看规则,再决定放行还是关掉。

这些都是排障最基础的工具,但要真正用好,得学会看输出的每一列代表什么。比如ss -antp输出里有LISTEN、ESTAB、TIME_WAIT等状态,TIME_WAIT大量堆积说明连接关闭频繁,多半是短连接过多,这时候调应用的连接池比关防火墙更有效。

6. 环境变量与 Shell 脚本入门:让命令自动化起来

6.1 环境变量到底是怎么生效的

环境变量是 Shell 和程序之间传递配置信息的通道。echo $PATH能看当前 PATH,export PATH=$PATH:/new/path能临时追加。但这里有个新手容易混乱的点:export只对当前 Shell 会话有效,关闭终端就没了。

要让环境变量永久生效,需要写进配置文件。不同发行版、不同 Shell 的配置加载顺序不一样,是新手很容易懵的地方。以 bash 为例,登录 shell 会依次加载/etc/profile、~/.bash_profile或~/.bash_login、~/.profile,非登录交互 shell 则加载~/.bashrc。CentOS 上通常会在~/.bash_profile里手动 source~/.bashrc,而 Ubuntu 则是/etc/profile里 source/etc/bash.bashrc。

我个人的建议是:把自定义 PATH 和别名写到~/.bashrc底部,因为非登录 shell(比如在 tmux 里新开窗口)也会加载它。写完后执行source ~/.bashrc立即生效,不用重新登录。像 Anaconda 的环境变量,装完之后提示你写进~/.bashrc,就是这么个道理。

6.2 自定义 PATH 与 JAVA_HOME 这类典型配置

在命令行里执行java -version时,Shell 其实是在 PATH 里找名为 java 的可执行文件。如果你装了 JDK 但没配 PATH,Shell 就找不到,直接报command not found。

典型配置方式是在~/.bashrc里:

export JAVA_HOME=/opt/jdk17 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib

注意 PATH 的赋值顺序:$JAVA_HOME/bin放在最前面,意思就是优先使用我们自己装的 JDK,而不是系统自带的 OpenJDK。这个“越靠前越优先”的规则,在排查“明明装了新版本但命令还是老版本”的问题时是关键。

检查是否生效:

which java java -version echo $PATH

6.3 第一个实用脚本:日志清理脚本

环境变量和命令掌握后,写一个简单的日志清理脚本是把知识串起来的最佳练习。我随便写一个监控并清理超过 N 天日志的脚本:

#!/bin/bash # 清理超过 7 天的日志文件 # 先打印将被删除的文件,人工确认后再取消注释执行删除 LOG_DIR="/var/log/myapp" DAYS=7 find "$LOG_DIR" -name "*.log" -type f -mtime +$DAYS -print # find "$LOG_DIR" -name "*.log" -type f -mtime +$DAYS -delete

这个脚本有几个值得说的点。-mtime +7表示修改时间在 7 天之前的文件,-type f限定只找普通文件,避免误删目录。先打印再删是我强烈建议的保险策略,生产环境直接删文件的脚本一定要先验证一遍输出结果。

更稳健的脚本会考虑磁盘占用率作为触发条件:

#!/bin/bash LOG_DIR="/var/log/myapp" THRESHOLD=80 usage=$(df -h "$LOG_DIR" | awk 'NR==2 {print +$5}') if [ "$usage" -gt "$THRESHOLD" ]; then find "$LOG_DIR" -name "*.log" -type f -mtime +3 -delete echo "磁盘使用率 ${usage}%,已清理 3 天前的日志。" else echo "磁盘使用率 ${usage}%,无需清理。" fi

df -h输出的第五列是使用率百分比,awk+$5把它转成数字参与比较。这个脚本加进 crontab 里每天执行一次,就能实现日志自动轮转,算是最简单的运维自动化实践。

6.4 crontab 调度任务入门

cron 是 Linux 自带的定时任务工具,crontab -e编辑当前用户的定时任务。格式是五个字段:分、时、日、月、周。举个例子:

# 每天凌晨 2 点执行备份脚本 0 2 * * * /home/user/bin/backup.sh # 每 5 分钟检查一次服务状态 */5 * * * * /home/user/bin/check_service.sh # 每周一早上 8 点清理临时目录 0 8 * * 1 /usr/bin/find /tmp -type f -mtime +7 -delete

配置好之后,日志会写到/var/log/cron或通过journalctl -u crond查看。如果定时任务没有执行,先排查 crond 服务是否在跑,再看脚本有没有执行权限、有没有写执行日志。

这里提醒一个常见的坑:cron 执行环境和我们手动登录终端的环境不一样,它没有加载~/.bashrc,所以脚本里如果用到了自定义 PATH 或者 JAVA_HOME,必须在脚本开头显式设置,否则就会出现“手动执行脚本没问题,但 cron 里跑就报 command not found”的诡异现象。

7. 常见故障排查实录:这些问题我踩过,你们别再踩了

7.1 故障案例 1:磁盘空间满了,但是删了文件还没释放

现象:df -h显示/分区使用率 100%,但du -sh /算下来远没到那么大。于是你以为自己算错了,但实际是文件系统上有文件被删除了,但还有进程持有它的文件句柄。

排查流程:

df -h du -sh /* # 找不到大头 lsof | grep deleted # 发现 /var/log/nginx/access.log (deleted) 还被 nginx 进程占用 systemctl restart nginx df -h # 空间释放了

这类问题最常见的场景就是日志文件:你rm了日志,但写入日志的进程还在运行,这个文件的实际数据块一直没释放。记住:删除文件不等于释放磁盘,还要确保没有进程继续持有该文件的句柄。

7.2 故障案例 2:程序一关终端就“消失”

现象:在远程 SSH 终端里启动了一个 Java 程序,一切正常。退出 SSH 再重连,发现进程没了,日志里也没有明显报错。

原因就是终端关闭后 SIGHUP 把进程带走了。解决办法就是用前面讲的nohup或setsid。更规范的做法是写成 systemd service,让 systemd 管理进程生命周期,这样不仅能开机自启,还能崩溃自动重启:

[Unit] Description=My Java App After=network.target [Service] Type=simple User=appuser ExecStart=/usr/bin/java -jar /opt/app/myapp.jar Restart=on-failure RestartSec=5 Environment=JAVA_HOME=/opt/jdk17 [Install] WantedBy=multi-user.target

把文件放到/etc/systemd/system/myapp.service,然后:

systemctl daemon-reload systemctl enable --now myapp

7.3 故障案例 3:明明有权限,但脚本执行报 Permission denied

现象:写了个deploy.sh,ls -l看权限也正常,但执行./deploy.sh就报Permission denied。

这个问题的原因多半是脚本没有执行权限。解决:

chmod +x deploy.sh ./deploy.sh

另一个变种是“执行到一半报 Permission denied”,那是脚本内部某个命令的执行权限不足,或者脚本尝试往没有写权限的目录写入文件。排查方式是把脚本一行行拆开执行,或者用bash -x deploy.sh开启调试模式,它会一条条打印执行的命令和结果,很容易定位是哪一步出错。

7.4 故障案例 4:端口被占用,服务起不来

现象:启动 Nginx 报错bind() to 0.0.0.0:80 failed (98: Address already in use)。

排查流程:

ss -tlnp | grep :80 # 看到 PID 后确认是哪个进程占着 80 端口 ps -p PID -o pid,comm,args # 如果是旧版 nginx 没杀干净 kill -15 PID # 还不退就 kill -9 systemctl start nginx

这里给一个容易踩坑的经验:如果端口上跑的是一个重要的线上服务,杀之前一定要确认清楚。有一次我遇到过 80 端口被一个异常启动的 Java 测试进程占了,杀完之后发现那竟然是另一个部门在用的内部服务,搞得大家虚惊一场。现在我的习惯是:杀进程前一定先ps -p看启动命令、看启动时间,确认是预期内的进程才动手。

7.5 故障排查速查表:对照这表能解决八成日常问题

现象可能原因排查命令解決思路
命令找不到PATH 没配或软件没装echo $PATH,which cmd重装或用绝对路径执行
磁盘满日志爆炸或大文件残留df -h,du -sh /*, `lsofgrep deleted`
内存不足应用泄漏或缓存太多free -h,top -o %MEM,ps aux --sort=-%mem重启应用或加内存
端口被占用进程残留或冲突ss -tlnp确认进程后 kill,或用其它端口
服务起不来配置错误或依赖缺失systemctl status -l,journalctl -u 服务名 -e看日志定位,修复配置
关终端进程就没了未脱离终端会话检查父进程,nohup启动用 nohup / systemd 管理
文件删了空间没释放有进程持有句柄`lsofgrep deleted`
时间不准NTP 失效或时区错误timedatectl,chronyc sources设置时区,重启 chronyd
cron 任务不执行环境变量缺失或服务没启动journalctl -u crond,crontab -l脚本内显式设置 PATH
网络不通防火墙或路由问题ping,ip route,firewall-cmd --list-all按链路逐层排查

8. 学习建议与扩展路径

day02 的内容量其实不小,从用户权限到进程管理,从文本处理到故障排查,每块都能展开成一篇独立的文章。我在实际学习时的一个体会是:不要追求“把所有命令背下来”,而是要追求“遇到问题能想到用什么工具”。命令是工具,场景化学习比死记硬背高效得多。

一个比较有效的方法是:在虚拟机里故意制造故障,然后自己排查。比如把 nginx 的端口改成 90,然后启动看报错;比如故意删掉一个系统日志文件,然后观察磁盘占用;比如写一个不断往内存写数据的 Python 脚本,然后用top观察系统怎么崩。这些“搞破坏”的练习,才是真正把知识变成能力的过程。

这套笔记之后可以往几个方向扩展:一是 Shell 脚本编程深入,把循环、函数、判断都用熟,写出一套完整的自动化脚本;二是服务部署实战,比如用 systemd 管理 Nginx、MySQL、Java 应用,搞明白服务开机自启的完整链路;三是网络进阶,把 tcpdump、iptables、路由策略这些搞明白,能独立排查跨服务器通信的问题。每个方向都能让 day02 这些基础发挥出指数级的价值。

最后说一个我个人的建议:日常工作中遇到不认识的命令,先用man查手册,再看/usr/share/doc里的示例,最后再上网搜。搜索引擎能解决 80% 的问题,但这 80% 里有一半是英文资料,所以英文阅读能力对 Linux 学习来说,是越早开始练习越好的投资。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询