OpenShell:开源Shell增强管理工具,让命令行任务可复用可调度
作为一个天天泡在终端里的人我一直在留意各种能提升命令行效率的开源工具。OpenShell这个项目名最近在开发者圈子里出现频率相当高它本质上是一个开源的Shell增强与管理工具把分散在多个终端窗口、脚本文件、任务计划里的重复操作收拢到一个可控、可复用、可调度的统一环境里。这篇文章我会结合自己实际折腾的过程把这个项目能做什么、适合谁、底层逻辑是什么、怎么配置、有哪些坑一次性说清楚。无论你是后端开发、运维工程师还是每天要跟批量文件打交道的普通用户都能从中找到可以直接拿来用的东西。1. 项目概览OpenShell到底解决什么问题1.1 为什么Shell工具被迫“增强”传统命令行工具其实有个很尴尬的处境单个命令很强大但组合起来全靠人肉记忆。我见过太多同事的终端窗口里历史记录往上翻几万行就为了找三天前跑过的那条带了一长串参数的备份命令。而脚本文件更是散落各处有时候同一套逻辑在项目目录、家目录、临时目录各有一份谁也不知道哪才是最新版本。换一台电脑或者换一个同事接手整套环境就成了一笔糊涂账。OpenShell这类项目想解决的就是把“Shell命令”从一个临时的、一次性的动作变成一个有名字、有参数、有日志、可重复调用的“任务资产”。它不是一个花哨的终端模拟器而是直接在Shell层面做了一层统一调度和管理。你可以把最常用的几十条命令打包成任务设置好默认参数和运行环境之后无论在哪个目录、哪台机器上只需要一个名字就能把完整逻辑跑起来。这个思路听起来简单但真正落地之后提升的不只是效率而是整个工作方式的确定性。1.2 一句话理解它的核心定位OpenShell的核心定位可以类比成汽车的中控台。没有中控台的时候你要分别去摸收音机旋钮、空调拨杆、车窗开关每个都是一套独立操作逻辑。中控台出现后所有功能被集成到一块面板上统一交互、统一状态显示。OpenShell对命令行的作用也是一样的它把那些各自为战的Shell命令、脚本片段、环境配置集中到一个工作台里让你用一套统一的方式去调用和管理。这个定位决定了它和普通Shell工具的区别。普通Shell强化工具专注的是终端本身好不好看、自动补全够不够聪明OpenShell更侧重的是“任务管理和环境统一”。这听起来有点抽象但你看它解决的问题就很具体今天要在生产环境跑一次数据同步明天要清理一周前的日志后天要给客户导一份报表。这三件事看起来毫不相关但本质上都是“在不同时间、不同环境、带着不同参数执行一段固定逻辑”。OpenShell就是把这些逻辑沉淀下来让你不用每次都从零敲命令。1.3 它适合谁不适合谁如果你属于下面这几类人那OpenShell值得花一个下午去折腾后端开发和运维需要频繁执行部署脚本、数据库备份、日志清理、服务健康检查最怕命令参数太长记不住。数据分析师和QA测试经常要跑固定的数据预处理流程或者自动化测试脚本需要统一入口、便于复用。需要远程管理多台服务器的工程师每次SSH进去都要重新敲一遍环境变量和常用别名OpenShell能帮你把环境上下文固化下来。有“脚本强迫症”的人希望所有脚本有统一的结构、统一的日志输出、统一的传参方式。反过来如果你只是偶尔在终端里敲两三条ls、cd、git status那这个工具对你就过剩了。它面向的是“命令行使用频率足够高、重复操作足够多”的场景。一个项目如果定位不准再好用也只是玩具OpenShell的定位恰恰卡在了“重度命令行用户”这个精确的群体上。2. 核心能力拆解它凭什么提高效率2.1 把命令升级为“命名任务”OpenShell最基础的能力是把一段命令或一个脚本注册成一个有名字的任务。比方说我每天都手动敲find /data/logs -type f -name *.log -mtime 7 -delete echo cleanup done这条命令本身不长但它有几个问题一是管道符和引号在键盘上不好打二是哪天换个目录就想不起来参数了。用OpenShell注册之后我只需要定义一次# 伪代码示例示意注册逻辑 openshell register cleanup_logs --cmd find /data/logs -type f -name *.log -mtime 7 -delete以后执行就是openshell run cleanup_logs就这么简单对就这么简单。但真正的价值在后面的细节里。OpenShell会帮你记录这个任务上次运行的时间、运行状态、输出内容片段。你在周报里写“本周完成日志清理自动化”的时候还能顺手附上执行记录这在日常工作留痕上特别有用。2.2 参数化与模板化的真正意义如果你只把OpenShell当成一个“命令别名增强工具”那就太亏了。它真正厉害的是参数化能力。同样一个日志清理任务你可能今天清测试环境的、明天清预发环境的、后天清生产环境的。传统做法是复制三份脚本改路径而OpenShell的做法是定义一份任务模板运行时注入参数openshell run cleanup_logs --path /data/logs_test --days 7 openshell run cleanup_logs --path /var/log/app --days 30同一个任务不同参数跑N次都不用手动改代码。关键是参数可以被记录在历史中下次想复用某个特定组合直接翻执行历史一键重跑。这一点对那种“每月月初要给财务导一次上月数据”的场景简直是救命级别的便利——不用再翻聊天记录找当时用的命令了直接OpenShell里跑历史记录。从原理上说参数化的本质是把“命令”和“数据”解耦。命令是稳定的逻辑数据是变化的输入。这个思路其实跟写函数是一模一样的不把逻辑写死而是通过入参控制行为。OpenShell把你的Shell命令提升到了“函数”的抽象级别这对习惯写代码的人而言几乎是直觉式的。2.3 会话保持告别每次重设环境我刚开始用终端的时候最烦的一件事就是每次打开新窗口都得重新设置环境变量、重新cd到工作目录、重新加载alias。OpenShell在会话保持上做得很到位。它会把每个任务的上下文环境固化下来包括默认工作目录需要加载的环境变量前置依赖检查条件输出日志的存放路径举个例子我有一个数据同步任务依赖Python虚拟环境和一堆环境变量。以前手动执行的话至少要四步激活虚拟环境、导出环境变量、cd到脚本目录、运行脚本。把这四步串起来注册成一个OpenShell任务之后以后每次运行只需要一个命令OpenShell自动恢复当时的上下文。这不仅仅是省了几次敲键盘的动作更是把“环境不一致导致脚本跑不通”这类问题从根源上掐掉了。2.4 统一日志与执行记录的价值很多命令行工具都有输出但输出只在终端里流动窗口一关就什么都没了。OpenShell会对每个任务生成一份带时间戳的执行记录输出、错误、退出码都被留存下来。这一点在排障时特别有用——很多问题不是当场就能定位的往往要翻历史执行记录才能看出规律。比如某个任务每次跑到一半就挂掉有了执行记录就能对比最近几次的环境差异定位是不是最近改了某个配置导致的。3. 实操落地从零开始跑通第一个OpenShell任务3.1 安装与环境准备先说明一下我下面写的是基于OpenShell常见发布形态的通用流程具体到你拿到手的版本可能会有一点差异但整体思路是通用的。第一步是获取项目文件。OpenShell这类开源项目一般通过Git仓库分发我习惯把它放在专门的工具目录下而不是扔在临时目录。下载之后进入项目目录先看README里的依赖要求通常需要本地已经安装了Git和某个版本的Python环境。确认依赖没问题后执行安装脚本或者make命令完成构建。装好之后跑一下版本验证命令能正常输出版本号就说明安装成功了。这里有一个容易被忽略的点安装完成后把它所在目录加入系统的PATH环境变量。否则每次使用都要写完整路径体验会大打折扣export PATH$HOME/opt/openshell/bin:$PATH把这行写进你的~/.bashrc或者~/.zshrc会让后续使用顺畅很多。这一步不是OpenShell特有的要求但很多人装完工具后觉得“不好用”十有八九是没配置PATH还停留在用完整路径调用的阶段。3.2 创建第一个任务从手动到自动装好之后我建议先选一个你日常操作频率最高的命令来练手。以我自己为例我第一个注册的任务是“清理临时文件”。平时手动敲的是rm -rf /tmp/build_cache echo cache cleared我知道直接rm -rf在END里风险很大但这就是真实工作中的场景。用OpenShell注册的时候先创建一个任务目录然后定义一个任务描述文件。这个文件一般用JSON或YAML格式声明任务名称、要执行的命令、默认参数、工作目录等信息。# 示例任务的描述文件 name: clean_build_cache command: rm -rf {{path}} echo cache cleared cwd: /tmp defaults: path: /tmp/build_cache保存之后通过OpenShell的命令加载这个任务然后执行。如果一切正常终端会输出和手动执行一样的结果。但不同的是这次执行留下了记录下次想再次清理时不需要再回忆路径了。跑通第一个任务之后我强烈建议你再注册一个带真实业务逻辑的任务比如执行一个Python脚本并给它加一个日期参数。这样你能真切感受到参数化带来的区别而不是停留在“哦它就是个包装命令”的浅层认知上。3.3 传参与路径设计细节里的魔鬼实操中最影响体验的往往是路径和参数的处理方式。这里分享几个我踩出来的经验一是路径尽量用相对路径配合任务定义的工作目录而不是在命令里硬编码绝对路径。绝对路径最大的问题是换机器就失效而OpenShell的任务定义里设置了cwd字段后命令里的相对路径就会自动基于工作目录解析天然可移植。二是参数默认值的设定逻辑。开发时为了方便调试默认值可以给得随意一点但上线到日常使用后默认值应该设置成“安全、保守”的那个选项避免误操作。比如清理类任务的默认保留天数我会设成30而不是7——就算有人忘记传参也不会误删太新的文件。三是输出内容的处理。有些任务执行时间很长可能要跑十几分钟。这种任务建议把输出重定向到日志文件同时OpenShell的执行记录里会自动保留一份。这样即使终端窗口关闭任务在后台也能继续跑日志也不怕丢。示例配置如下command: python3 sync_data.py --start {{start_date}} --end {{end_date}} {{log_dir}}/sync_{{start_date}}.log 21这里用到了两个参数日期范围来自用户输入日志目录来自任务默认配置。这样每次执行都会生成一个带日期的日志文件归档检索非常方便。3.4 用一个综合场景串联全流程光讲单个功能没感觉我拿一个真实的综合场景串一遍批量日志采集。背景我需要每天凌晨对三台服务器的访问日志做压缩归档并且生成一份统计报告。以前的做法是手动SSH到每台机器执行一段压缩命令再把文件拉回本地用脚本统计完发送报告。整套流程下来接近二十分钟而且经常因为某个步骤顺序乱了导致结果不对。用OpenShell重新组织之后我把它拆成三个任务collect_logs负责远程拉取日志文件到本地目录。archive_and_count对拉取到的日志进行打包并统计总量、错误码分布。send_report把生成的统计报告通过邮件发出去。每个任务单独注册参数只有日期。然后OpenShell内置的任务编排功能可以把这三个任务串联成一个组任务openshell run-group daily_log_pipeline --date 2025-04-10一次执行三个环节自动衔接。任何一个环节失败后续任务都不会执行而且失败原因会在执行记录里留下完整输出。这套流程上线后我的操作时间从二十分钟缩短到一条命令而且结果稳定性大幅提升。这不仅仅是“自动化”而是把整个操作过程变成了一个可追踪、可回溯、可交接的标准流程。4. 常见问题与排查技巧实录4.1 高频问题速查表用OpenShell的过程中我遇到过不少问题也帮同事排过一些坑。整理成一张速查表方便你对照排查。现象可能原因排查与解决思路任务执行后没有输出命令中的输出被重定向到了文件或解释器没找到检查是否配置了日志文件先去掉重定向参数单独执行测试提示“command not found”注册任务时指定的命令路径不对或PATH中缺失在任务定义中显式写明解释器的绝对路径如/usr/bin/python3任务定义了但找不到任务目录没有被正确加载到索引确认加载命令是否执行查看配置里的任务根目录是否正确运行结果和手动执行不一致环境上下文不同比如缺少环境变量在任务定义中增加env字段明确设置关键环境变量中文内容显示乱码终端编码和日志文件编码不一致统一使用UTF-8编码在命令前加export LANGen_US.UTF-8超长时间任务卡死命令需要交互式确认等待用户输入在命令中加上非交互选项比如yes、-y、--non-interactive参数没传进去参数名拼写不一致或引号使用不当仔细对比任务定义里的参数名和命令行传入的参数名注意大小写和前后缀4.2 三个容易踩的细节坑第一个坑是隐藏字符问题。有时候从网页或聊天工具里复制一段命令到任务定义文件会带进来不可见的特殊字符。这些字符在编辑器里看不出来但执行时会莫名其妙报错。我排查这类问题的经验是遇到诡异报错先别急着看逻辑直接用cat -A查看任务定义文件的原始内容特殊字符会直接显示出来。第二个坑是符号链接带来的路径歧义。如果你的任务工作目录是一个符号链接那么在这个目录下解析相对路径时结果可能跟你预期的不一样。比如/data链接到/mnt/storage/data在/data下的相对路径解析结果和直接在真目录里是有差异的。规范做法是在任务定义里统一使用真实路径尽量避免符号链接层叠。第三个坑是同时运行同一个任务的并发问题。OpenShell默认允许同一个任务并发执行但在某些场景下这会造成脏数据。比如你的任务会往同一个日志文件里追加内容并发时就会互相覆盖或交错。我的做法是在任务定义里加上单例运行限制确保同一时刻只有一个实例在跑。如果没有这个选项就在命令外层加一个锁文件控制这也是通用的做法。4.3 调试技巧临时开启详细日志OpenShell的日志机制很关键。平时它的输出比较收敛但排查问题时要敢于打开详细日志模式。常见的做法是设置环境变量或者在运行命令时加--verbose参数打开后会输出每一步的详细过程包括任务匹配、参数解析、环境准备、命令执行等。这些信息对定位问题极为重要。很多时候问题根本不在命令本身而是在OpenShell的任务解析阶段——参数类型不对、字段名拼错、配置文件格式错误这些在详细日志里都会明确指出来。5. 项目价值与我的个人体会5.1 对个人开发者的实际提升从个人效率角度来看OpenShell带来的最大改变不是“敲命令变快了”而是“思考命令的方式变了”。以前我要在心里维护一张隐形的命令清单靠记忆驱动现在我把它变成显式的任务列表靠逻辑驱动。这种转变很微妙但效果很实在我的工作记忆被释放了不用再惦记那些复杂命令的细节。尤其是维护多个项目时每个项目的常用操作都被固化成一组任务切换项目只需要切换任务组而不是在历史命令里翻找。5.2 对团队协作的隐性价值OpenShell对团队协作的价值往往比个人效率更容易被低估。你可以把任务定义文件放到Git仓库里团队每个人克隆下来就能拥有完全一致的命令行操作入口。新人入职不再需要被塞一份几十页的命令操作手册而是打开OpenShell看任务列表就知道日常操作有哪些、怎么跑。这种用“接口统一”代替“人传人经验”的做法本质上和代码库一样是在积累可复用的工程资产。当然前提是团队愿意花一点时间去维护任务定义的质量那种随手写、不含参数校验的任务还是别提交进去丢人了。5.3 实测后的心里话最后聊点实际的感受。我当初开始接触OpenShell纯粹是出于好奇心想着“这不就是给命令起个别名吗”。真正用了一个月之后我发现自己再也回不去了所有日常任务都跑在OpenShell里甚至包括那些当年我觉得“直接用Shell敲也行”的场景。它不完美文档也有不少需要自己摸索的地方但它的设计方向是对的。开源项目最迷人的地方就是你跟它一起成长遇到问题可以自己改源码PR被合并的成就感比功能本身更有吸引力。如果你正在被一堆绕来绕去的命令困扰不妨花半天时间把OpenShell跑起来从第一个任务开始你会感受到那种“终于有人帮我把琐碎收起来”的舒坦。

相关新闻

i2 Analyst‘s Notebook 8教程:话单与资金流关联分析实战

i2 Analyst‘s Notebook 8教程:话单与资金流关联分析实战

简介:这是一份IBM i2 Analysts Notebook 8的中文培训教材,面向安全执法、金融调查、反欺诈、情报研判等领域的数据分析人员,系统讲解如何使用该工具对通话记录、银行交易、人员物品关系等复杂数据进行可视化关联与深度挖掘。资源共1个doc文档…

2026/10/14 21:25:07 阅读更多 →
【无人机控制】动态系统多无人机协同控制搜索跟踪(目标在二维空间内运动、转向、碰撞反)【含Matlab源码 16074期】

【无人机控制】动态系统多无人机协同控制搜索跟踪(目标在二维空间内运动、转向、碰撞反)【含Matlab源码 16074期】

💥💥💥💥💥💥💥💥💞💞💞💞💞💞💞💞💞Matlab领域博客之家💞&…

2026/10/14 21:25:07 阅读更多 →
表单提交了,但什么都没发生:3 个隐藏必填、2 秒 Toast 与后台标签的静默丢弃

表单提交了,但什么都没发生:3 个隐藏必填、2 秒 Toast 与后台标签的静默丢弃

表单提交了,但什么都没发生:3 个隐藏必填、2 秒 Toast 与后台标签的静默丢弃自动化脚本的最后一公里,往往是最安静的一段:标题填了、正文灌了、发布按钮也点了——然后网络面板里一个请求都没有。没有报错,没有超载&am…

2026/10/14 21:25:07 阅读更多 →

最新新闻

低代码与手写代码的边界:哪些场景该用低代码?

低代码与手写代码的边界:哪些场景该用低代码?

1. “手写代码已老”这句话,到底在哪条赛道上才成立我们上一家公司有个内部审批系统,开发团队花了三周做出来,上线两周后又因为审批流程调整要改代码,改完还要发版、走发布窗口,业务方在群里催了三次,研发负…

2026/10/15 6:09:41 阅读更多 →
ESLint报错Possible race condition解析:Vue异步竞态条件与修复方案

ESLint报错Possible race condition解析:Vue异步竞态条件与修复方案

第一次在CI日志里看到这行报错时,我一度以为是某个第三方ESLint插件新出的规则。后来翻规则文档才发现,这个提示Vue项目里高频出现的Possible race condition: xxxx might be reassigned based on an outdated value of xxxx,其实来自 ESLint…

2026/10/15 6:09:41 阅读更多 →
PyTorch深度学习实战:从动态图机制到模型部署全流程解析

PyTorch深度学习实战:从动态图机制到模型部署全流程解析

开篇得先说实话:我接触 PyTorch 的时间不算最早,但用下来这些年,它成了我主力深度学习框架。如果你正打算入门深度学习、或者从其他框架转过来,又或者已经在用但总感觉有些地方“知其然不知其所以然”,这篇东西就是写给…

2026/10/15 6:09:41 阅读更多 →
大厂RAG为何弃用纯向量检索?混合检索与多路召回实战解析

大厂RAG为何弃用纯向量检索?混合检索与多路召回实战解析

1. 从一个被问烂了的问题说起“为什么大厂 RAG 从不用纯向量检索?”这个问题我在不同场合被问过不下十次。有刚入行的朋友,有做了几年搜索转行做 RAG 的工程师,也有产品经理拿着技术方案来对需求。每次我都得从头讲一遍,因为这里面…

2026/10/15 6:09:41 阅读更多 →
射频信号发生器选型与实操:SMB100B与SMW200A对比解析

射频信号发生器选型与实操:SMB100B与SMW200A对比解析

做射频调试这行,信号发生器是桌面上最不该含糊的仪器之一。罗德与施瓦茨的SMB100B和SMW200A,我在好几个项目的测试环境里都遇到过:前者是典型的单通道模拟信号源,适合干净连续波和常规模拟调制;后者则是双通道矢量信号…

2026/10/15 6:09:41 阅读更多 →
AI编程协作的工程实践:责任边界、风险防控与团队规范

AI编程协作的工程实践:责任边界、风险防控与团队规范

1. 这不是“AI写代码”,而是工程师与工具的重新分工最近在几个技术社区里,总看到有人发截图:一段Python脚本,三行注释,AI生成了200行带单元测试的完整模块;也有人贴出生产事故报告,核心服务因AI…

2026/10/15 6:08:41 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/14 7:53:34 阅读更多 →