news 2026/10/24 5:23:45

【FDE系列】阶段2:Day 39:Linux 综合实战 — 让服务无人值守

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【FDE系列】阶段2:Day 39:Linux 综合实战 — 让服务无人值守

📚前言

📒FDE系列内容总纲:

【大纲】FDE 前沿部署工程师学习系列教程-CSDN博客

🚄前置课程列表:

见文档结尾附录。


🚀阶段2·Day 39:Linux 综合实战 — 让服务无人值守

FDE 学习系列教程 · 第二阶段 · 第 4 周 · Day 4预计时长:2.5 小时 | 难度:★★★☆☆ | 前置知识:Day 36-38 全部内容

📌一句话目标:用 nohup / screen 让 FastAPI 后台常驻,用 tail -f 盯日志,用 crontab 配置定时备份和健康检查,实现服务挂了自动重启。


🧑‍🤝‍🧑 开场:你不可能 24 小时守着终端

先想一个问题:你的 FastAPI 现在是怎么跑的?

uvicorn main:app --reload

它占着当前终端窗口——窗口一关、SSH 一断、电脑一锁屏,服务立刻没了。这在开发时没问题,但在客户现场是致命的:客户的系统得 7×24 小时跑着,你总不能让一台电脑永远开着一个黑窗口、还不许碰。

今天解决四件事,让服务具备"无人值守"能力:

┌────────────────────────────────────────────────────────┐ │ ① 后台常驻:关终端/断 SSH,服务照样跑(nohup/screen) │ │ ② 实时盯日志:服务在后台干了啥,随时能看(tail -f) │ │ ③ 定时任务:备份/巡检到点自动执行(crontab) │ │ ④ 自愈:服务挂了,健康检查脚本自动重启 │ └────────────────────────────────────────────────────────┘

这就是把一个"开发模式的程序"变成"服务器上常驻服务"的关键一步。下周学 Docker 之前,先用最朴素的方式体验一遍"部署"这件事。


📖 一、先理解:为什么关终端程序就会死?

要理解 nohup,先得知道一个概念——信号(Signal)。

而nohup的字面意思就是no hangup(别挂断)——让程序忽略 SIGHUP 信号,终端关了它也装作没听见,继续跑。


🖥️ 二、nohup:让服务后台常驻

准备:确保项目就绪

cd ~/fde_project ls # main.py venv/ source venv/bin/activate

确保main.py里有/health接口(健康检查要用),最小版本:

from fastapi import FastAPI import uvicorn app = FastAPI() @app.get("/") def root(): return {"msg": "FDE API running in background"} @app.get("/health") def health(): return {"status": "ok"} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)

一条命令后台启动

nohup uvicorn main:app --host 0.0.0.0 --port 8000 > app.log 2>&1 &

别看这一行短,每个符号都有讲究,逐个拆解:

nohup 忽略挂断信号,关终端不死 uvicorn main:app --host 0.0.0.0 --port 8000 你的服务命令 > app.log 标准输出(1)写进 app.log 2>&1 错误输出(2)合并到标准输出(1) → 正常日志和报错都进 app.log & 扔到后台运行,终端立刻还给你

敲完回车,你会看到类似:

[1] 9876 ← [1]是后台任务号,9876 是进程 PID nohup: ignoring input and redirecting stderr to stdout

终端立刻可以继续用了——服务在后台跑着。

验证服务真的活着

# ① 用 curl 访问 curl http://localhost:8000/ # {"msg":"FDE API running in background"} curl http://localhost:8000/health # {"status":"ok"} # ② 查进程 ps aux | grep uvicorn | grep -v grep # fde 9876 ... uvicorn main:app --host 0.0.0.0 --port 8000 # ③ 看日志 cat app.log

终极测试:关掉这个终端窗口

直接关掉Ubuntu 窗口,然后重新开一个:

ps aux | grep uvicorn | grep -v grep # 进程还在! curl http://localhost:8000/health # 服务照样响应! # {"status":"ok"}

这就是后台常驻。这一步成功,你会真切体会到"部署"和"开发运行"的区别。

停止后台服务

# 找到 PID 再 kill(标准做法) ps aux | grep uvicorn | grep -v grep kill 9876 # 或者一条命令按名字杀 pkill -f "uvicorn main:app" # 确认端口释放 ps aux | grep uvicorn | grep -v grep # 没输出 = 已停止

📖 三、screen:给服务开一个"关不掉的房间"

nohup 有个缺点:服务扔后台后,你没法再回到它的交互界面。screen 解决了这个问题——它能创建一个可以随时"进出"的终端会话。

┌────────────────────────────────────────────────────┐ │ screen 就像一个"独立的终端房间": │ │ │ │ 你走进房间,启动 uvicorn │ │ 按 Ctrl+A, D 暂时离开房间(detach,服务继续跑) │ │ 关电脑/断网都不影响房间里的程序 │ │ 随时 screen -r 回到房间,服务的画面原封不动 │ └────────────────────────────────────────────────────┘ 对比 nohup: nohup = 把程序扔后台,只能通过日志文件看输出 screen = 程序在房间里跑,随时能"推门进去"看实时界面

安装与使用

sudo apt install -y screen # ① 创建一个名叫 fde_api 的会话并进入 screen -S fde_api # 屏幕会清一下,你已经在"房间"里了,正常启动服务(前台方式即可) cd ~/fde_project source venv/bin/activate uvicorn main:app --host 0.0.0.0 --port 8000 # ② 脱离会话:先按住 Ctrl+A,松开,再按一下 D # (detach)。你回到了原始终端,但服务还在房间里跑 # 底部会提示:[detached from 9876.fde_api] # ③ 查看当前有哪些会话 screen -ls # There is a screen on: # 9876.fde_api (09/18/26 15:00:00) (Detached) # ④ 重新进入会话(resume) screen -r fde_api # 你会看到 uvicorn 的实时输出画面,跟离开时一模一样 # ⑤ 彻底结束会话:进入后 Ctrl+C 停掉服务,再输入 exit # 或者从外部直接杀掉:screen -X -S fde_api quit

选 nohup 还是 screen?

场景

推荐

就想让服务安静地跑,输出全进日志

nohup(简单,配日志文件足够)

需要随时回去看程序的实时交互界面

screen

要同时挂多个需要观察的任务

开多个 screen 会话,命名区分

生产环境长期稳定运行

systemd 服务(更正规,本系列后续阶段讲)

💡 FDE 在客户现场临时部署、跑长任务(数据迁移、批量脚本),screen 极其常用——断 SSH 不怕,重连screen -r接着看。今天 nohup 和 screen 都会用即可。


🖥️ 四、tail -f:实时盯后台日志

服务进了后台,怎么知道它在干嘛、有没有报错?不能 cat 一下看一下,得用实时追踪:

tail -f app.log # 屏幕停住不动,app.log 一旦有新内容立刻刷出来 # 新开另一个终端 curl 几次接口,这边能看到请求日志实时滚动 # Ctrl+C 退出追踪(只退出查看,不影响服务)

tail -f 的进阶组合

# 只盯着错误看(管道过滤) tail -f app.log | grep ERROR # 同时盯错误和警告(-E 正则) tail -f app.log | grep -E "ERROR|WARNING" # 先看最后 50 行,再继续实时追踪 tail -n 50 -f app.log # 同时追踪多个日志文件 tail -f app.log backup.log

📌 排障标准动作记住:服务后台跑着 → 新开终端tail -f app.log→ 另一个终端复现操作 → 实时观察日志输出。两个终端配合,问题无所遁形。


📖 五、crontab:让任务到点自己跑

备份脚本写好了,难道每天凌晨 2 点爬起来手动敲?当然不用。crontab是 Linux 内置的"定时任务调度器",你写好时间表,它到点自动执行。

crontab 基本命令

crontab -e # 编辑当前用户的定时任务表(第一次会让你选编辑器,选 nano 即可) crontab -l # list:查看当前所有定时任务 crontab -r # remove:清空所有定时任务(⚠️ 慎用)

cron的特殊符号

符号含义例
*任意值* * * * *每分钟
,枚举0,30 * * * *每半点
-范围0 9-18 * * *9~18 点整
/步长*/10 * * * *每 10 分钟

特殊字符串(快捷写法)

字符串等价于
@reboot开机启动时执行一次
@hourly0 * * * *
@daily0 0 * * *
@weekly0 0 * * 0
@monthly0 0 1 * *
@yearly0 0 1 1 *

cron 时间表的五颗星

这是 cron 的核心语法,一条任务长这样:

┌───────── 分钟 (0-59) │ ┌─────── 小时 (0-23) │ │ ┌───── 日 (1-31) │ │ │ ┌─── 月 (1-12) │ │ │ │ ┌─ 星期 (0-7,0和7都是周日) │ │ │ │ │ * * * * * 要执行的命令

对着例子理解最快:

0 2 * * * 每天凌晨 2:00 整 */5 * * * * 每 5 分钟 30 8 * * 1-5 每周一到周五的 8:30 0 */2 * * * 每 2 小时(0点、2点、4点...) 0 0 1 * * 每月 1 号的 0:00 30 9 * * 6 每周六上午 9:30 * * * * * 每分钟(调试时临时用)
┌────────────────────────────────────────────────────────┐ │ 符号速查: │ │ * 每一(每个时间单位都匹配) │ │ */5 每隔 5(能整除时触发) │ │ 1,3,5 枚举:第1、3、5 │ │ 1-5 范围:1 到 5 │ └────────────────────────────────────────────────────────┘

配置两个实战定时任务

先把昨天的备份脚本放到固定位置并确保有执行权限:

chmod +x ~/fde_lab/backup.sh mkdir -p ~/backups ~/logs

执行crontab -e,在文件末尾加入:

# 每天凌晨 2:00 备份 fde_project,日志追加到 backup.log 0 2 * * * /home/fde/fde_lab/backup.sh /home/fde/fde_project /home/fde/backups >> /home/fde/logs/backup.log 2>&1 # 每 5 分钟做一次服务健康检查(health_check.sh 下一节写) */5 * * * * /home/fde/fde_lab/health_check.sh >> /home/fde/logs/health.log 2>&1

保存退出(nano:Ctrl+O 回车保存,Ctrl+X 退出),看到crontab: installing new crontab就生效了。

crontab -l # 确认任务已写入

cron 的三个血泪坑

⚠️坑 1:命令必须用绝对路径!

cron 执行时没有你登录后的 PATH 环境,uvicorn、python3这些命令可能"找不到"。所以:

  • 脚本路径写全:/home/fde/fde_lab/backup.sh,不能写./backup.sh

  • 脚本内部用到的命令也尽量写绝对路径,或在脚本开头加source ~/.bashrc/ 手动设 PATH

⚠️坑 2:输出一定要重定向到文件

cron 任务没有终端,输出没地方去(有些系统会给你发本地邮件,多数直接丢弃)。务必像上面那样>> xxx.log 2>&1,否则出了错你啥都看不到。

⚠️坑 3:cron 不会自动激活虚拟环境

你的 uvicorn 装在 venv 里,cron 直接调会找不到。健康检查/启动脚本里要先source /home/fde/fde_project/venv/bin/activate,或直接用 venv 里的绝对路径:/home/fde/fde_project/venv/bin/uvicorn。

不用等到凌晨:临时验证任务

把备份任务临时改成*/2 * * * *(每 2 分钟),等两分钟后检查:

ls -lt ~/backups/ # 看是否生成了新备份包 cat ~/logs/backup.log # 看备份日志

验证完记得改回0 2 * * *,别让它每 2 分钟备份一次把磁盘塞满。


🖥️ 六、健康检查脚本:服务挂了自动拉起来

这是今天的综合产物。思路很朴素:定时探测服务 → 活着就记一笔 → 连续探测失败就重启。

新建health_check.sh:

#!/bin/bash # ========================================== # 健康检查 + 自动重启脚本 # 由 crontab 每 5 分钟调用一次 # ========================================== set -euo pipefail # ---------- 配置区 ---------- API_URL="http://localhost:8000/health" MAX_RETRY=3 # 最多重试次数 WAIT_SECONDS=10 # 每次重试间隔 PROJECT_DIR="/home/fde/fde_project" VENV_UVICORN="/home/fde/fde_project/venv/bin/uvicorn" # --------------------------- log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" } RETRY=0 while [ $RETRY -lt $MAX_RETRY ]; do # curl -s 静默 -f HTTP 错误码也算失败;输出丢进黑洞,只看退出码 if curl -sf "$API_URL" > /dev/null 2>&1; then log "✅ 服务正常" exit 0 # 探测成功,脚本结束 fi RETRY=$((RETRY + 1)) log "⚠️ 第 ${RETRY}/${MAX_RETRY} 次探测失败,${WAIT_SECONDS} 秒后重试..." sleep "$WAIT_SECONDS" done # 三次都失败,判定服务挂了 → 重启 log "🚨 连续 ${MAX_RETRY} 次探测失败,判定服务异常,开始重启..." cd "$PROJECT_DIR" # 先清理可能残留的旧进程(防止端口占用) pkill -f "uvicorn main:app" 2>/dev/null || true sleep 2 # 用 venv 里的 uvicorn 绝对路径后台启动 nohup "$VENV_UVICORN" main:app --host 0.0.0.0 --port 8000 \ > app.log 2>&1 & sleep 5 # 等服务启动 # 重启后再验证一次 if curl -sf "$API_URL" > /dev/null 2>&1; then log "🔄 服务已成功重启并恢复正常" else log "❌ 重启后服务仍不可用,需要人工介入!" exit 1 fi
  • set -euo pipefail

严格模式,让脚本"遇错早退",是健壮脚本的关键:

选项作用
-e任何命令返回非 0 就立即退出
-u使用未定义变量时报错退出
-o pipefail管道中任一环节失败,整条管道算失败

注意:后面多处用|| true显式"忽略失败",正是为了绕开-e。

为什么要"连续 3 次"?避免偶发抖动(如一次 GC、瞬时高负载)误触发重启。

  • 全部用绝对路径——因为 cron 环境PATH很精简,相对路径会找不到。

  • MAX_RETRY × WAIT_SECONDS = 30 秒,即单次 cron 运行最多容忍 30 秒不可用才重启。

  • curl -sf参数含义(关键)

    参数作用
    -ssilent,静默,不输出进度条
    -ffail,HTTP 状态码 ≥400 时返回非 0(否则 500 也会被当成功)

    配合> /dev/null 2>&1丢弃输出,只看退出码:0=健康,非 0=异常。

    循环逻辑

  • 探测成功 → 打 ✅,exit 0结束(不重启)。

  • 失败 → 计数 +1,打 ⚠️,sleep 10后重试。

  • 最多 3 次。注意:第 3 次失败后仍会 sleep 10 秒再跳出循环(轻微浪费,但无害)。

为什么要"重试 3 次"而不是一次失败就重启?

探测失败 ≠ 服务一定挂了,可能是: · 服务正在处理一个慢请求,响应慢了 · 系统那一瞬间 CPU 繁忙 · 网络抖了一下 所以设计成"连续 3 次、每次间隔 10 秒都失败"才判定死亡, 避免一次抖动就误杀好进程。这叫"容错/防抖"思想。

实测自愈效果

chmod +x health_check.sh # ① 确保 cron 已配置(*/5 那行),或先手动跑一次验证脚本本身没问题 ./health_check.sh # 2026-09-18 15:10:00 ✅ 服务正常 # ② 模拟服务挂掉:手动杀掉 uvicorn pkill -f "uvicorn main:app" curl http://localhost:8000/health # 已经连不上了 # ③ 立即手动跑健康检查(不用等 5 分钟),观察自愈全过程 ./health_check.sh # ⚠️ 第 1/3 次探测失败,10 秒后重试... # ⚠️ 第 2/3 次探测失败,10 秒后重试... # ⚠️ 第 3/3 次探测失败,10 秒后重试... # 🚨 连续 3 次探测失败,判定服务异常,开始重启... # 🔄 服务已成功重启并恢复正常 # ④ 再验证 curl http://localhost:8000/health # {"status":"ok"} 活了!

今天的完整部署全景


📝 本课小结

能力

工具/命令

关键点

后台常驻

nohup 命令 > log 2>&1 &

忽略 SIGHUP,输出进日志,& 放后台

会话管理

screen -S 名/-r 名/ Ctrl+A,D

创建/进入/脱离可恢复的终端房间

看实时日志

tail -f app.log

配合 grep 只看错误

停止服务

pkill -f/kill PID

先找进程再杀

定时任务

crontab -e/-l

五颗星:分 时 日 月 周

备份定时

0 2 * * * 脚本 >> log 2>&1

每天 2 点

巡检定时

*/5 * * * * 脚本

每 5 分钟

健康检查

curl -sf URL看退出码

失败重试 3 次再重启

cron 三坑

绝对路径/重定向/venv

一个都不能忘

🧠 核心认知:让服务"无人值守"靠的是一套组合拳——nohup/screen 解决"不死",日志解决"可观测",crontab 解决"自动触发",健康检查解决"自愈"。这正是生产环境运维思想的缩影(虽然真正的生产环境会用 systemd/Docker/监控平台,但核心逻辑今天你已经亲手实现了一遍)。


📋 课后练习

练习 1:nohup 后台验证(约 20 分钟)

  1. 用 nohup 启动 FastAPI,确认app.log生成、curl能访问

  2. 关掉终端窗口,重开后确认进程还在(ps aux | grep)

  3. 用tail -f app.log在另一个终端实时看请求日志,然后用 curl 打几次/health,观察日志滚动

  4. 用 pkill 停掉服务

练习 2:screen 会话(约 15 分钟)

  1. 创建名为work的 screen 会话,在里面运行top

  2. 用 Ctrl+A,D 脱离,screen -ls确认会话 Detached

  3. screen -r work回到会话,确认 top 画面还在,最后退出并关闭会话

练习 3:crontab + 健康检查(约 30 分钟)

  1. 配置 cron:每分钟把当前时间追加到~/logs/min.log(* * * * * date >> ~/logs/min.log),等 2-3 分钟后tail验证,验证后删除该任务

  2. 配置每天凌晨 2 点的备份任务,用crontab -l确认

  3. 手动杀掉 uvicorn,运行health_check.sh,观察"3 次重试 → 自动重启 → 恢复"全过程,并查看health.log


🔭 下节预告

今天你的服务已经能后台常驻、定时备份、挂了自愈,像个真正的服务器程序了。明天是本周收官,把 Shell 脚本打磨到"生产级":

  • set -euo pipefail严格模式:为什么你的脚本明明出错了还傻乎乎往下跑?

  • trap 信号处理:脚本被 Ctrl+C 打断时,如何自动清理临时文件

  • case 语句:写一个./service.sh start|stop|status|restart这样的服务管理脚本,一键掌控服务

最后做本周总复习和验收。明天见!


🌍附录:前置课程列表

阶段一:认知启蒙(AI 认知与 FDE 角色)
AI 认知

【FDE系列】阶段1Day 1:AI 层级关系 — 四个嵌套的圈-CSDN博客

【FDE系列】阶段1Day 2:AI 三阶段发展史 — 会认 → 会判断 → 会创造-CSDN博客

【FDE系列】阶段1Day 3:符号 AI vs 机器学习 — 两条路线的本质区别-CSDN博客

【FDE系列】阶段1Day 4:Transformer 的历史意义 — 2017 年的分水岭-CSDN博客

【FDE系列】阶段1Day 5:本周复习与自测 — 检验你的 AI 认知地基-CSDN博客

【FDE系列】阶段1Day 6:Transformer 架构 — 一张图纸盖出千千万万栋楼-CSDN博客

【FDE系列】阶段1Day 7:LLM 本质 — 文字接龙机器-CSDN博客

【FDE系列】阶段1Day 8:Token — 模型眼中的最小单位-CSDN博客

【FDE系列】阶段1Day 9:AI 幻觉 — 为什么会一本正经地胡说八道-CSDN博客

【FDE系列】阶段1Day 10:上下文窗口 — 模型的记忆力上限 + 本周复习-CSDN博客

【FDE系列】阶段1Day 11:Prompt — 给模型立规矩-CSDN博客

【FDE系列】阶段1Day 12:Memory — 让模型记住上下文

【FDE系列】阶段1Day 13:RAG — 给模型配图书管理员-CSDN博客

【FDE系列】阶段1Day 14:Tool Use — 让模型动手操作-CSDN博客

【FDE系列】阶段1Day 15:MCP — 统一的工具接口标准 + 第三周复习-CSDN博客

FDE 基础概念

【FDE系列】阶段1Day 16:什么是 FDE — 把 AI 变成客户结果的人-CSDN博客

【FDE系列】阶段1Day 17:FDE vs 传统实施 — 三大本质区别-CSDN博客

【FDE系列】阶段1Day 18:FDE 三重身份 + C6 胜任力模型-CSDN博客

【FDE系列】阶段1Day 19:七阶段行动路径 + 行业经验的价值-CSDN博客

【FDE系列】阶段1Day 20:阶段总结与产出物 — 第一阶段收官-CSDN博客


阶段二:技术地基(Python + FastAPI + SQL + Docker + API 集成)
Python基础

【FDE系列】阶段2:Day 21:Python 环境搭建 — 写出你的第一行代码-CSDN博客

【FDE系列】阶段2:Day 22:变量、数据类型、条件判断 — Python 的“记忆“和“判断“-CSDN博客

【FDE系列】阶段2:Day 23:循环与函数 — 让代码跑 100 遍、把逻辑打包复用-CSDN博客

【FDE系列】阶段2:Day 24:数据结构 — 列表、字典、集合、元组-CSDN博客

【FDE系列】阶段2:Day 25:文件读写与 JSON — 让程序连通外部数据(第一周收官)-CSDN博客

【FDE系列】阶段2:Day 26:模块化编程 — 把代码拆成“抽屉柜“-CSDN博客

【FDE系列】阶段2:Day 27:异常处理与日志 — 让程序“摔不烂、查得到“-CSDN博客

FastAPI入门到进阶

【FDE系列】阶段2:Day 28:FastAPI 入门 — 把你的函数变成 API 服务-CSDN博客

【FDE系列】阶段2:Day 29:FastAPI 进阶 — Pydantic 模型与完整 CRUD 实战-CSDN博客

【FDE系列】阶段2:Day 30:生产代码规范 — 测试、类型注解、配置管理(第二周收官)-CSDN博客

SQL基础

【FDE系列】阶段2:Day 31:SQL 基础 — 增删改查一把梭-CSDN博客

【FDE系列】阶段2:Day 32:多表查询 — JOIN 与聚合-CSDN博客

【FDE系列】阶段2:Day 33:进阶查询 — 窗口函数与 CTE-CSDN博客

【FDE系列】阶段2:Day 34:数据清洗 — 把脏数据捋干净-CSDN博客

【FDE系列】阶段2:Day 35:Python + SQL — 工单接入 MySQL + 本周收官-CSDN博客

【FDE系列】阶段2:Day 36:Linux 入门与文件操作 — 扔掉鼠标的第一天-CSDN博客

【FDE系列】阶段2:Day 37:权限、进程与文本三剑客-CSDN博客

【FDE系列】阶段2:Day 38:Shell 脚本 — 把命令串起来自动跑-CSDN博客

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 5:23:45

【FDE系列】阶段2:Day 37:权限、进程与文本三剑客

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 5:18:43

硬盘分区与文件系统全解析:从MBR到LVM

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 5:18:33

动态规划在餐饮优化中的应用:UVa 11633解析

1. 项目背景与问题定义UVa 11633 "Food Portion Size"是弗吉尼亚大学计算机科学系在算法课程中使用的经典编程题目,属于动态规划(Dynamic Programming)领域的典型应用场景。这道题目模拟了餐饮行业中的实际经营问题——如何通过科学…

作者头像 李华
网站建设 2026/10/24 5:11:34

VMware安装Ubuntu全攻略:从资源规划到快照排错

虚拟机里跑 Ubuntu,这件事我前前后后折腾了不下几十次,从早期动不动就卡在安装界面,到后来闭着眼睛都能把整套流程走完,中间踩过的坑基本能写一本小册子。很多人第一次接触虚拟机,最直接的需求就是想要一个干净、隔离、…

作者头像 李华
网站建设 2026/10/24 5:09:54

基于Copernicus与机器学习的SPEI干旱滚动预测实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 5:09:32

Web自动化测试中注入JavaScript的实用场景与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华