首页
关于
Search
1
git lg彩色显示日志
29 阅读
2
在 Ubuntu 22.04 LTS 中安装 Docker
19 阅读
3
CentOs/Ubuntu搭建上网x-ui
18 阅读
4
git使用多个源和多个分支
15 阅读
5
git保存账号密码
15 阅读
默认分类
网站搭建
Windows
Linux
Docker
OpenWrt
Hackintosh
Git
Python
Web开发
JavaScript
FFmpeg
Demo
工具
刷机
油猴脚本
Excel
Chrome Extension
登录
Search
标签搜索
Pandas
读取
时区
Chrome
centos8
求和
Nginx
Typecho
404
csv
国际站
询盘导出
油猴脚本
bbr
Ubuntu
远程桌面
日志
log
数据清洗
打印机
野生程序猿
累计撰写
155
篇文章
累计收到
0
条评论
首页
栏目
默认分类
网站搭建
Windows
Linux
Docker
OpenWrt
Hackintosh
Git
Python
Web开发
JavaScript
FFmpeg
Demo
工具
刷机
油猴脚本
Excel
Chrome Extension
页面
关于
搜索到
155
篇与
的结果
2026-04-10
Linux 时间同步(NTP)快速配置
1. 查看状态timedatectl status2. 开启自动同步timedatectl set-ntp true3. 设置时区(示例)timedatectl set-timezone Asia/Shanghai4. 再次确认timedatectl status
2026年04月10日
1 阅读
0 评论
0 点赞
2026-03-31
OpenWrt PPPoE 自动检测 CGNAT 并重拨
OpenWrt PPPoE 自动检测 CGNAT 并重拨(精简版 v3)适用于:PPPoE 拨号用户目标:避免获取 100.x.x.x(CGNAT)IP特点:只检测 WAN IP,无公网出口检测,适合配合 OpenClash 使用🚀 功能特点✅ 自动检测 WAN IP 是否为私网 / CGNAT✅ Hotplug 实时触发(ifup + ifupdate)✅ Cron 定时兜底✅ 防死循环(最大重试次数)✅ 防并发(文件锁)✅ 冷却时间,避免频繁重拨✅ 日志清晰🧩 一、主脚本路径:/root/wan_auto_redial.sh内容:#!/bin/sh # ====== 锁(防并发)====== LOCK_FILE="/tmp/wan_redial.lock" exec 200>$LOCK_FILE flock -n 200 || { logger -t WAN-REDIAL "Another instance is running, exit" exit 0 } # ====== 配置 ====== INTERFACE="wan" MAX_RETRY=3 COOLDOWN=60 # 重拨最小间隔(秒) COUNT_FILE="/tmp/wan_redial_count" LAST_REDIAL_TIME_FILE="/tmp/last_redial_time" # ====== 获取当前时间 ====== NOW=$(date +%s) # ====== 冷却机制 ====== LAST_TIME=$(cat $LAST_REDIAL_TIME_FILE 2>/dev/null || echo 0) if [ $((NOW - LAST_TIME)) -lt $COOLDOWN ]; then logger -t WAN-REDIAL "Cooldown active, skip" exit 0 fi # ====== 获取WAN IP ====== WAN_IP=$(ubus call network.interface.$INTERFACE status | jsonfilter -e '@["ipv4-address"][0].address') logger -t WAN-REDIAL "WAN IP: $WAN_IP" [ -z "$WAN_IP" ] && exit 0 # ====== 判断私网IP(CGNAT) ====== is_private_ip() { echo "$1" | grep -qE '^(100\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)' } NEED_REDIAL=0 if is_private_ip "$WAN_IP"; then logger -t WAN-REDIAL "Detected private/CGNAT IP, will redial" NEED_REDIAL=1 fi # ====== 重拨逻辑 ====== if [ "$NEED_REDIAL" -eq 1 ]; then COUNT=$(cat $COUNT_FILE 2>/dev/null || echo 0) if [ "$COUNT" -lt "$MAX_RETRY" ]; then COUNT=$((COUNT+1)) echo $COUNT > $COUNT_FILE echo $NOW > $LAST_REDIAL_TIME_FILE logger -t WAN-REDIAL "Redial attempt $COUNT/$MAX_RETRY..." ifdown $INTERFACE sleep 5 ifup $INTERFACE else logger -t WAN-REDIAL "Max retries reached, stop redial" fi else # 正常 WAN IP → 重置计数 echo 0 > $COUNT_FILE logger -t WAN-REDIAL "WAN IP OK, no redial needed" fi🔧 二、赋予执行权限chmod +x /root/wan_auto_redial.sh⚡ 三、Hotplug 触发路径:/etc/hotplug.d/iface/99-wan-redial内容:#!/bin/sh [ "$INTERFACE" = "wan" ] || exit 0 [ "$ACTION" = "ifup" -o "$ACTION" = "ifupdate" ] || exit 0 logger -t WAN-REDIAL "Hotplug triggered: ACTION=$ACTION" /root/wan_auto_redial.sh权限:chmod +x /etc/hotplug.d/iface/99-wan-redial⏱️ 四、Cron 兜底(5分钟一次)crontab -e添加:*/5 * * * * /root/wan_auto_redial.sh📊 五、日志查看查看日志:logread | grep WAN-REDIAL实时查看:logread -f | grep WAN-REDIAL🧠 六、工作流程拨号成功 / WAN IP 变化 ↓ hotplug 触发(ifup / ifupdate) ↓ 脚本执行(带锁) ↓ 检测 WAN IP 是否私网 ↓ 满足条件 → 执行 ifdown/ifup 重拨 ↓ 最多 3 次 + 冷却控制✅ 七、特性总结(精简版)只检查 WAN IP 是否私网(100.x / 10.x / 172.16-31 / 192.168.x)冷却机制防止频繁重拨最大 3 次重拨防死循环Hotplug + Cron 双触发,保证稳定性文件锁防止并发执行适合 OpenClash 出口管理备注:公网 IP 检测已去掉,所有判断基于 WAN IP 本地信息即可。
2026年03月31日
1 阅读
0 评论
0 点赞
2025-12-27
一次讲清:宽字节、多字节、UTF-8、GBK、Unicode、代码页(从原理到 Windows/VC6 实战)
这篇文章的目标只有一个:建立不再被“乱码”反复折磨的工程级认知模型。全文从“字符是什么”讲到“Windows 为什么会乱”,再落到 VC6/老项目的可执行结论,一次性讲清,不靠零散记忆,不靠运气。一、乱码的本质只有一句话乱码不是字符错了,而是“同一串字节,被用错了规则去解释”。任何文本问题,都可以还原成下面这条链路:字符含义 → 字符集 → 编码方式 → 字节序列 → 再被解释只要编码方式和解释方式不一致,就一定乱码。二、字符集(Character Set):先规定“有哪些字”字符集解决的是:世界上有哪些字符,以及它们的编号是什么。常见字符集包括:ASCII、GB2312、GBK、GB18030,而现代世界几乎都以 Unicode 为核心。Unicode 的本质不是编码,它只是一个全球统一的编号表(Code Point):“中” → U+4E2D“国” → U+56FD😀 → U+1F600Unicode 只负责“编号”,不负责怎么存成字节。三、编码方式(Encoding):把编号变成字节编码方式解决的是:Unicode 编号如何存储成二进制字节。UTF-8Unicode 的一种编码方式1~4 字节变长完全兼容 ASCII网络和跨平台事实标准例:“中” Unicode:U+4E2D UTF-8:E4 B8 AD(3 字节)UTF-16Unicode 的另一种编码方式2 或 4 字节Windows 内部原生使用C/C++ 中对应 wchar_t例:“中” UTF-16LE:2D 4EGBK中文本地编码1 或 2 字节不是 Unicode强依赖“解释环境”例:“中” GBK:D6 D0四、宽字节 vs 多字节(这是 Windows 语境)这是Windows 编程术语,不是编码学术概念。多字节(MBCS)使用 char*一个字符占用字节数不固定GBK、UTF-8、Shift-JIS 都是多字节多字节 ≠ UTF-8宽字节(Wide)使用 wchar_t*Windows 下固定为 UTF-16一个 wchar_t = 2 字节五、ANSI 是什么(99% 乱码根源)ANSI 不是一种编码。在 Windows 里:ANSI = “当前系统代码页下的多字节解释规则”所有 xxxA API 的真实行为都是:char* → 按当前代码页(CP_ACP) → 转 Unicode → 再处理六、代码页(Code Page):解释字节的“字典”代码页决定:char* 到底按什么规则被解释。常见代码页:936:GBK(简体中文)65001:UTF-81252:西欧CP_ACP 表示当前 ANSI 代码页,由系统区域设置决定。七、Windows 并不存在“UTF-8 进程”这是一个非常重要的纠正点。Windows 内部统一使用 UTF-16(W API)。 所谓“UTF-8 系统”,只是:把 CP_ACP 改成了 65001让 所有 A API 按 UTF-8 解释 char*W API 永远不受影响。八、VC6 / 老项目乱码的形成公式源码保存编码 ≠ 运行时 CP_ACP ≠ API 解释规则↓乱码典型炸点包括:源码是 GBK,系统 CP_ACP = 65001源码是 UTF-8,系统 CP_ACP = 936DLL 被不同宿主进程加载使用 MessageBoxA / CreateFileA / RegSetValueExA九、为什么 UTF-8 Beta 会“突然把老程序搞炸”Windows 的 “Beta: 使用 Unicode UTF-8 提供全球语言支持” 会:把 CP_ACP 改成 65001所有 A API 改为按 UTF-8 解 char*但老程序仍在传 GBK 字节,于是必然乱码。系统没错,是老程序的隐含假设错了。十、工程级铁律(记住这 4 句就够)char* 没有编码,只有字节编码信息存在于“解释方”,不在字符串本身A API = 按代码页猜W API = 唯一确定行为十一、VC6 / 老项目的最终正确路线在不重构整个项目的前提下,唯一长期稳定方案是:内部 char* 统一 UTF-8与系统交互全部使用 W API在边界做明确转换(MultiByteToWideChar / WideCharToMultiByte)不再依赖 CP_ACP不再关心宿主是 GBK 还是 UTF-8这套方案不靠系统设置、不靠用户环境、不靠运气。十二、结语乱码从来不是“中文太复杂”,而是规则被混用了。 只要你在工程中做到:一处确定、处处确定,乱码问题会从此消失。这篇文章本质上是在帮你建立一个判断标准:“这串字节,是谁、按什么规则,在解释它?”一旦这个问题你能随时回答,乱码就再也不是问题。
2025年12月27日
2 阅读
0 评论
0 点赞
2025-12-18
深入理解 Git rebase:如何把“私有补丁”永久叠加在 dev 之上
Git 实战:持续合并 dev 更新,并让私有代码始终位于最后提交在实际开发中,我们经常会遇到这样一种场景:dev 分支在持续更新(主线开发)我们基于 dev 派生了一个 定制分支 / 魔改分支 / 私有功能分支该分支中存在 dev 分支永远不会合入的私有代码需求是:持续同步 dev 的最新更新,但始终保证私有代码位于提交历史的最后,不被 dev 覆盖这种需求在以下场景中非常常见:OEM 定制版本客户专用功能分支内部增强版 / 魔改版长期维护的补丁分支本文将介绍一种 Git 原生、稳定、可长期使用 的解决方案。一、问题本质分析我们先抽象一下分支结构:dev: A -- B -- C -- D \ feature: E -- F (私有代码)我们的目标是:dev 每增加新提交(D → E → F → G…)feature 能持续同步 dev同时满足:私有提交 始终在最末尾提交历史尽量保持线性、干净二、核心思路:把私有代码当成“补丁”Git 中有一个非常适合这个需求的命令:rebaserebase 的本质语义是:将一组提交重新应用到另一个基点之上因此我们可以把整个问题理解为:dev 是不断前进的“主线”私有代码是一组需要长期叠加在主线之上的“补丁”三、推荐方案:使用 rebase 固定私有提交在最后适用前提当前分支可以 rebase(个人分支 / 约定可改历史)私有代码与 dev 的修改边界相对清晰1️⃣ 拉取 dev 最新代码git fetch origin2️⃣ 找到 dev 与当前分支的分叉点git merge-base feature origin/dev该命令会输出一个 commit hash,记为:<BASE_COMMIT>它是 feature 与 dev 的最后一个公共祖先。3️⃣ 核心命令:将私有提交重放到 dev 之上git rebase --onto origin/dev <BASE_COMMIT> feature这条命令做了什么?等价于:将 feature 分支中从 <BASE_COMMIT> 之后的所有提交重新应用到 origin/dev 的最新提交之上4️⃣ rebase 后的分支结构dev: A -- B -- C -- D \ feature: E' -- F'📌 私有提交始终位于最后四、以后 dev 再更新,怎么同步?如果私有代码始终只存在于 feature 分支中,后续同步将非常简单:git checkout feature git fetch origin git rebase origin/devGit 会自动:先对齐最新的 dev再把你的私有提交 重新应用到最后五、冲突会发生在哪里?这是该方案的一个重要优势:冲突 只会发生在私有提交中dev → dev 的提交之间不会产生冲突冲突范围清晰、可控非常适合 长期维护型分支。六、注意事项(重要)⚠️ 1. rebase 会改写提交历史请确保:当前分支是个人维护分支或团队已明确约定该分支可以 rebase如果已经推送过远程,需要使用:git push --force-with-lease⚠️ 2. 私有代码应尽量拆分成独立提交建议实践:一个私有功能一个 commit不要把私有代码与 dev 修改混在同一个提交中这样可以:降低 rebase 冲突概率提高长期可维护性七、不能 rebase 的替代方案(补充)在以下场景中,rebase 可能不适用:多人同时在该分支协作分支已被下游依赖,不能改历史此时可以使用:merge + 固定“补丁提交”策略约定规则:feature 分支最后一个提交永远是私有补丁同步流程:git checkout feature git fetch origin git merge origin/dev如遇冲突,可优先保留当前分支:git checkout --ours . git commit缺点是:提交历史不够线性merge commit 较多但在受限条件下依然可用。八、进阶工程实践:私有代码独立分支模型在企业级项目中,常见如下结构:origin/dev | feature-base (仅同步 dev,不写私有代码) | feature-private (只包含私有功能)同步流程:git checkout feature-base git rebase origin/dev git checkout feature-private git rebase feature-base该模式适合:OEM / 客户定制长期多版本维护私有代码体量较大场景九、总结Git 完全可以做到:dev 持续更新私有代码永远位于最后提交历史清晰、冲突可控核心思想只有一句话:把私有代码当成“补丁”,用 rebase 永远叠加在 dev 之上如果你有需要,也可以把这套流程封装成脚本,实现 一键同步 dev,非常适合长期项目维护。
2025年12月18日
2 阅读
0 评论
0 点赞
2025-09-11
自动把 requirements.txt 中的库锁死版本
在 Python 项目中,开发完成后直接部署,如果不锁定依赖版本,很可能因为底层库升级导致代码出错。因此,写死依赖版本是保证可重复部署的最佳实践。问题假设你的 requirements.txt 里有:requests numpy pandas如果不指定版本:pip install -r requirements.txt当库发布新版本时,部署环境可能与开发环境不一致可能出现 API 不兼容或行为改变的问题Python 脚本(跨平台)import subprocess # 读取原始 requirements.txt with open("requirements.txt") as f: lines = [line.strip() for line in f if line.strip() and not line.startswith("#")] # 获取当前环境已安装库及版本 installed = subprocess.run(["pip", "freeze"], capture_output=True, text=True).stdout.splitlines() installed_dict = dict(line.split("==") for line in installed if "==" in line) # 更新版本,只锁定文件中已有库 new_lines = [] for pkg in lines: pkg_name = pkg.split("==")[0] version = installed_dict.get(pkg_name) if version: new_lines.append(f"{pkg_name}=={version}") else: new_lines.append(pkg) # 未安装的保持原样 # 写回 requirements.txt with open("requirements.txt", "w") as f: f.write("\n".join(new_lines) + "\n") print("requirements.txt 已更新为锁定版本!")Linux / macOS 一行命令(bash)while read pkg; do v=$(pip show $pkg | grep Version | awk '{print $2}') if [ -n "$v" ]; then echo "$pkg==$v" else echo "$pkg" fi done < requirements.txt > requirements.locked.txt && mv requirements.locked.txt requirements.txt
2025年09月11日
2 阅读
0 评论
0 点赞
1
2
...
31