docs: 把被削成单字的动词补回完整词语
用户可见文案里有一批动词被缩成了单字,读起来像电报。统一补全, 菜单标签、帮助文本、提示信息、README 一起过。 装 → 安装 验 → 校验 下 → 下载 起 → 启动 跑 → 运行 拉 → 拉取 删 → 删除 换 → 更换 改 → 修改 查 → 查询 敲 → 输入 重跑 → 重新运行 启停 → 启动 / 停止 自启 → 开机自启 不开 → 不开启 复验 → 重新校验 没法 → 无法 落盘 → 写入磁盘 顺带修掉批量替换造成的重复词 (安安装 / 更更换 / 重写入服务文件 / 已打开开机自启),并补了一条通用自查: 用带 lookbehind 的正则扫任意 汉字连续重复,以及扫非「安装」的孤立「装」。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpjLPV4JvAsuq9ACb3u6B4
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
# mihomo-up
|
# mihomo-up
|
||||||
|
|
||||||
一个 bash 脚本,在 Linux 服务器上装好、配好、管住 [mihomo](https://github.com/MetaCubeX/mihomo) 内核(Clash.Meta 的继任者,MetaCubeX 维护的代理内核)。
|
一个 bash 脚本,在 Linux 服务器上安装好、配置好、管起来 [mihomo](https://github.com/MetaCubeX/mihomo) 内核(Clash.Meta 的继任者,MetaCubeX 维护的代理内核)。
|
||||||
|
|
||||||
一条命令从零到能用:下载内核 → 写 systemd 服务 → 用向导生成 `config.yaml` → 启动。之后改订阅、换端口、升级、回滚、卸载也都在同一个菜单里。
|
一条命令从零到能用:下载内核 → 写入 systemd 服务 → 用向导生成 `config.yaml` → 启动。之后修改订阅、更换端口、升级、回滚、卸载也都在同一个菜单里。
|
||||||
|
|
||||||
装完,这台机器上会多出这几样东西——**卸载时它们全都能一键清掉**:
|
安装完成后,这台机器上会多出这几样东西——**卸载时它们全都能一键清掉**:
|
||||||
|
|
||||||
| | 默认位置 |
|
| | 默认位置 |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -21,22 +21,22 @@
|
|||||||
|
|
||||||
- Linux + bash 4 以上
|
- Linux + bash 4 以上
|
||||||
- 依赖命令:`curl`、`gzip`、`install`、`awk`、`sed`(一般发行版自带)
|
- 依赖命令:`curl`、`gzip`、`install`、`awk`、`sed`(一般发行版自带)
|
||||||
- 安装、升级、改配置、卸载都需要 **root**。非 root 也能进菜单,会先提醒一句
|
- 安装、升级、修改配置、卸载都需要 **root**。非 root 也能进菜单,会先提醒一句
|
||||||
- 没有 systemd 的机器(容器等)也能用:脚本跳过服务相关步骤,只管内核和配置,你自己 `mihomo -d /etc/mihomo` 拉起来
|
- 没有 systemd 的机器(容器等)也能用:脚本跳过服务相关步骤,只管内核和配置,你自己 `mihomo -d /etc/mihomo` 启动起来
|
||||||
|
|
||||||
交互输入读的是 `/dev/tty` 而不是标准输入,所以 `bash <(curl ...)` 这种 stdin 已被占用的跑法,菜单照样能按键。
|
交互输入读的是 `/dev/tty` 而不是标准输入,所以 `bash <(curl ...)` 这种 stdin 已被占用的运行方式,菜单照样能按键。
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
**交互菜单**(推荐,装机和日常维护都用它):
|
**交互菜单**(推荐,首次安装和日常维护都用它):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl -fsSL https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.sh)
|
bash <(curl -fsSL https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
菜单第 1 项就是一键安装。跑完你会拿到:本机的 HTTP/SOCKS5 代理端口、一个 Web 面板地址和它的访问密钥、一个开机自启的 systemd 服务。
|
菜单第 1 项就是一键安装。运行完你会拿到:本机的 HTTP/SOCKS5 代理端口、一个 Web 面板地址和它的访问密钥、一个开机自启的 systemd 服务。
|
||||||
|
|
||||||
还会拿到一个 `mihomo-up` 命令——**以后直接敲它就能进菜单**,不用再翻上面这条 URL。
|
还会拿到一个 `mihomo-up` 命令——**以后直接输入它就能进菜单**,不用再翻上面这条 URL。
|
||||||
|
|
||||||
**命令行**:
|
**命令行**:
|
||||||
|
|
||||||
@@ -44,41 +44,41 @@ bash <(curl -fsSL https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.
|
|||||||
chmod +x mihomo-up.sh
|
chmod +x mihomo-up.sh
|
||||||
|
|
||||||
sudo ./mihomo-up.sh install # 一键安装:内核 + systemd + 配置向导 + 启动
|
sudo ./mihomo-up.sh install # 一键安装:内核 + systemd + 配置向导 + 启动
|
||||||
./mihomo-up.sh check # 只查不改:当前版本 vs 最新版本
|
./mihomo-up.sh check # 只查询不改动:当前版本 vs 最新版本
|
||||||
sudo ./mihomo-up.sh update # 只换内核二进制,配置和服务定义不动
|
sudo ./mihomo-up.sh update # 只更换内核二进制,配置和服务定义不动
|
||||||
sudo ./mihomo-up.sh config # 重新跑配置向导
|
sudo ./mihomo-up.sh config # 重新运行配置向导
|
||||||
sudo ./mihomo-up.sh rollback # 内核回滚到上一个备份
|
sudo ./mihomo-up.sh rollback # 内核回滚到上一个备份
|
||||||
sudo ./mihomo-up.sh uninstall # 卸载,会逐项问你配置和备份删不删
|
sudo ./mihomo-up.sh uninstall # 卸载,会逐项问你配置和备份要不要删除
|
||||||
sudo ./mihomo-up.sh shortcut # 只装/刷新 mihomo-up 快捷命令
|
sudo ./mihomo-up.sh shortcut # 只安装 / 刷新 mihomo-up 快捷命令
|
||||||
```
|
```
|
||||||
|
|
||||||
## 菜单里有什么
|
## 菜单里有什么
|
||||||
|
|
||||||
```
|
```
|
||||||
1. 一键安装 内核 + systemd + 配置,缺哪步补哪步
|
1. 一键安装 内核 + systemd + 配置,缺哪步补哪步
|
||||||
2. 检查更新 3. 升级内核 4. 装指定版本内核
|
2. 检查更新 3. 升级内核 4. 安装指定版本内核
|
||||||
5. 回滚内核 6. 查看内核备份
|
5. 回滚内核 6. 查看内核备份
|
||||||
7. 配置管理 换订阅 / 改端口 / 局域网共享 / 面板密钥 / 导入配置 / 校验 / 编辑 / 备份还原
|
7. 配置管理 更换订阅 / 修改端口 / 局域网共享 / 面板密钥 / 导入配置 / 校验 / 编辑 / 备份还原
|
||||||
8. 服务控制 启停 / 重启 / 状态 / 最近日志 / 开机自启 / 重写服务文件
|
8. 服务控制 启动 / 停止 / 重启 / 状态 / 最近日志 / 开机自启 / 重写服务文件
|
||||||
9. 卸载
|
9. 卸载
|
||||||
```
|
```
|
||||||
|
|
||||||
## 一键安装做了什么
|
## 一键安装做了什么
|
||||||
|
|
||||||
1. **下内核**:按机器架构从 GitHub Releases 取对应的 `.gz` 并解压
|
1. **下载内核**:按机器架构从 GitHub Releases 下载对应的 `.gz` 并解压
|
||||||
2. **验内核**:新二进制必须能执行,且 `-v` 自报的版本号和目标一致,否则不落盘
|
2. **校验内核**:新二进制必须能执行,且 `-v` 自报的版本号和目标一致,否则不写入磁盘
|
||||||
3. **配文件**:没有 `config.yaml` 就跑配置向导;已经有了就保持不动,只问一句要不要重做
|
3. **准备配置**:没有 `config.yaml` 就运行配置向导;已经有了就保持不动,只问一句要不要重做
|
||||||
4. **写服务**:按 [MetaCubeX 官方文档](https://wiki.metacubex.one/startup/service/)的推荐写 `mihomo.service`。已存在且内容不同的话,旧文件先备份成 `.bak-<时间戳>`
|
4. **写入服务**:按 [MetaCubeX 官方文档](https://wiki.metacubex.one/startup/service/)的推荐写 `mihomo.service`。已存在且内容不同的话,旧文件先备份成 `.bak-<时间戳>`
|
||||||
5. **装快捷命令**:问一句,可以拒绝。它是什么见「快捷命令 `mihomo-up`」一节
|
5. **安装快捷命令**:问一句,可以拒绝。它是什么见「快捷命令 `mihomo-up`」一节
|
||||||
6. **起服务**:`daemon-reload` → `enable`(开机自启)→ `restart`,起不来就提示你看 `journalctl`
|
6. **启动服务**:`daemon-reload` → `enable`(开机自启)→ `restart`,启动不了就提示你看 `journalctl`
|
||||||
|
|
||||||
每一步都是「缺什么补什么」:已经装过的机器再跑一遍,不会覆盖你现有的配置。
|
每一步都是「缺什么补什么」:已经安装过的机器再运行一遍,不会覆盖你现有的配置。
|
||||||
|
|
||||||
装完会打印一段总结:代理端口怎么用、面板地址和密钥是什么、日志怎么看。
|
安装完成后会打印一段总结:代理端口怎么用、面板地址和密钥是什么、日志怎么看。
|
||||||
|
|
||||||
## 快捷命令 `mihomo-up`
|
## 快捷命令 `mihomo-up`
|
||||||
|
|
||||||
一键安装会往 `/usr/local/bin/mihomo-up` 放一个启动器。它**不是脚本本体,而是每次执行都去拉最新脚本再跑**——你更新了仓库,机器上下次敲 `mihomo-up` 就自动用上新版,不需要谁去手动更新。
|
一键安装会往 `/usr/local/bin/mihomo-up` 放一个启动器。它**不是脚本本体,而是每次执行都去拉取最新脚本再运行**——你更新了仓库,机器上下次输入 `mihomo-up` 就自动用上新版,不需要谁去手动更新。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mihomo-up # 进菜单,参数原样透传
|
mihomo-up # 进菜单,参数原样透传
|
||||||
@@ -87,25 +87,25 @@ mihomo-up check --json # 子命令照样能用,退出码也透传
|
|||||||
|
|
||||||
和纯粹的"每次重新下载"不同的是,它会把拉到的脚本落一份缓存到 `/var/lib/mihomo-up/`,并在这两种情况下回落到缓存:
|
和纯粹的"每次重新下载"不同的是,它会把拉到的脚本落一份缓存到 `/var/lib/mihomo-up/`,并在这两种情况下回落到缓存:
|
||||||
|
|
||||||
- **拉不到**(网络断了、仓库挂了)。这条对代理管理脚本尤其要紧:你最需要它的时候,往往正是代理挂了、网络不正常的时候,那时候还得能进菜单重启服务、看日志。
|
- **拉取失败**(网络断了、仓库挂了)。这条对代理管理脚本尤其要紧:你最需要它的时候,往往正是代理挂了、网络不正常的时候,那时候还得能进菜单重启服务、看日志。
|
||||||
- **拉到的东西没通过 `bash -n` 语法检查**(上游推了个坏版本,或者中间设备塞回来一个门户登录页)。宁可用旧的,也不以 root 执行来路不明的半个脚本。
|
- **拉到的东西没通过 `bash -n` 语法检查**(上游推了个坏版本,或者中间设备塞回来一个门户登录页)。宁可用旧的,也不以 root 执行来路不明的半个脚本。
|
||||||
|
|
||||||
抓取带 5 秒连接 / 20 秒总超时,网络卡住不会让菜单一直转着等。
|
抓取带 5 秒连接 / 20 秒总超时,网络卡住不会让菜单一直转着等。
|
||||||
|
|
||||||
fork 之后想让快捷命令指向你自己那份,装的时候加 `--script-url <你的 raw 地址>`,或者设环境变量 `MIHOMO_UP_URL`。
|
fork 之后想让快捷命令指向你自己那份,安装的时候加 `--script-url <你的 raw 地址>`,或者设环境变量 `MIHOMO_UP_URL`。
|
||||||
|
|
||||||
> **cron 里别用它**:每次执行都联网拉脚本,等于给定时任务多加一个失败点。定时任务直接指向缓存那份 `/var/lib/mihomo-up/mihomo-up.sh`,或者你自己固定的副本。
|
> **cron 里别用它**:每次执行都联网拉取脚本,等于给定时任务多加一个失败点。定时任务直接指向缓存那份 `/var/lib/mihomo-up/mihomo-up.sh`,或者你自己固定的副本。
|
||||||
|
|
||||||
## 配置向导问什么
|
## 配置向导问什么
|
||||||
|
|
||||||
向导生成的是一份**最小可用配置**,够跑,不臃肿:
|
向导生成的是一份**最小可用配置**,够用,不臃肿:
|
||||||
|
|
||||||
| 问题 | 说明 |
|
| 问题 | 说明 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 订阅链接 | 机场给的 http(s) 地址,写成 `proxy-providers`,每 24 小时自动更新一次 |
|
| 订阅链接 | 机场给的 http(s) 地址,写成 `proxy-providers`,每 24 小时自动更新一次 |
|
||||||
| 本地代理端口 | `mixed-port`,HTTP 和 SOCKS5 共用,默认 7890 |
|
| 本地代理端口 | `mixed-port`,HTTP 和 SOCKS5 共用,默认 7890 |
|
||||||
| 局域网共享 | `allow-lan`,开了同网段其它设备也能连(还要放行防火墙) |
|
| 局域网共享 | `allow-lan`,开了同网段其它设备也能连(还要放行防火墙) |
|
||||||
| 管理接口 | 只允许本机 / 局域网公网可访问 / 不开。面板和 API 都连这里 |
|
| 管理接口 | 只允许本机 / 局域网公网可访问 / 不开启。面板和 API 都连这里 |
|
||||||
| 访问密钥 | 回车即自动生成随机串;管理接口开在 `0.0.0.0` 时务必设 |
|
| 访问密钥 | 回车即自动生成随机串;管理接口开在 `0.0.0.0` 时务必设 |
|
||||||
| Web 面板 | 选是就写 `external-ui-url`,内核首次启动自己去下载 metacubexd |
|
| Web 面板 | 选是就写 `external-ui-url`,内核首次启动自己去下载 metacubexd |
|
||||||
| 分流规则 | 全部走代理(默认),或者指定某个国家/地区直连、其余走代理 |
|
| 分流规则 | 全部走代理(默认),或者指定某个国家/地区直连、其余走代理 |
|
||||||
@@ -114,22 +114,22 @@ fork 之后想让快捷命令指向你自己那份,装的时候加 `--script-u
|
|||||||
|
|
||||||
选了「指定国家/地区直连」会额外写一条 `GEOIP,<两位国家代码>,DIRECT`,代码填 `CN`、`US`、`JP`、`DE` 都行,小写会自动转大写。
|
选了「指定国家/地区直连」会额外写一条 `GEOIP,<两位国家代码>,DIRECT`,代码填 `CN`、`US`、`JP`、`DE` 都行,小写会自动转大写。
|
||||||
|
|
||||||
**改配置不用手写 YAML**:菜单「配置管理」能直接换订阅、改端口、开关局域网共享、改面板密钥。
|
**修改配置不用手写 YAML**:菜单「配置管理」能直接更换订阅、修改端口、开关局域网共享、改面板密钥。
|
||||||
|
|
||||||
每次改动之前自动备份成 `config.yaml.bak-<时间戳>`,改完立刻用内核 `-t` 校验。**校验不过会问你要不要还原**,服务在跑的话再问一句要不要重启生效。
|
每次改动之前自动备份成 `config.yaml.bak-<时间戳>`,改完立刻用内核 `-t` 校验。**校验不过会问你要不要还原**,服务正在运行的话再问一句要不要重启生效。
|
||||||
|
|
||||||
需要手写的复杂配置(DNS、TUN、自定义规则)请用菜单里的「手动编辑配置」,或者用「从 URL 导入完整配置」整份换掉——这两条路同样有备份、校验、还原兜底。
|
需要手写的复杂配置(DNS、TUN、自定义规则)请用菜单里的「手动编辑配置」,或者用「从 URL 导入完整配置」整份换掉——这两条路同样有备份、校验、还原兜底。
|
||||||
|
|
||||||
## 升级和回滚
|
## 升级和回滚
|
||||||
|
|
||||||
`update` 只换内核二进制,不动你的配置和服务定义。替换前有两道闸门,任一不过就中止:
|
`update` 只更换内核二进制,不动你的配置和服务定义。替换前有两道闸门,任一不过就中止:
|
||||||
|
|
||||||
1. 新二进制能跑起来,且 `-v` 自报的版本号和目标一致
|
1. 新二进制能运行起来,且 `-v` 自报的版本号和目标一致
|
||||||
2. 用**新版本**去 `-t` 校验你**现有的**配置
|
2. 用**新版本**去 `-t` 校验你**现有的**配置
|
||||||
|
|
||||||
第二道是为了挡住「新内核不认旧配置」这种升完才发现的问题。配置本来就有毛病、但你只想换内核时,加 `--skip-conf-check` 跳过它。
|
第二道是为了挡住「新内核不认旧配置」这种升完才发现的问题。配置本来就有毛病、但你只想更换内核时,加 `--skip-conf-check` 跳过它。
|
||||||
|
|
||||||
替换前旧内核备份到 `/var/backups/mihomo/`(保留最近 3 份)。升级后服务起不来会**自动回滚**到旧版并重启,然后提示你看 `journalctl -u mihomo -n 50`。
|
替换前旧内核备份到 `/var/backups/mihomo/`(保留最近 3 份)。升级后服务启动不了会**自动回滚**到旧版并重启,然后提示你看 `journalctl -u mihomo -n 50`。
|
||||||
|
|
||||||
任何一步失败都不会留下半个系统:要么没动,要么已回滚。
|
任何一步失败都不会留下半个系统:要么没动,要么已回滚。
|
||||||
|
|
||||||
@@ -137,16 +137,16 @@ fork 之后想让快捷命令指向你自己那份,装的时候加 `--script-u
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo ./mihomo-up.sh uninstall # 逐项确认
|
sudo ./mihomo-up.sh uninstall # 逐项确认
|
||||||
sudo ./mihomo-up.sh uninstall --purge # 全删,不问
|
sudo ./mihomo-up.sh uninstall --purge # 全部删除,不询问
|
||||||
```
|
```
|
||||||
|
|
||||||
直接删掉:内核二进制、`mihomo.service` 及其备份、`mihomo.service.d/` drop-in 目录、快捷命令和它的缓存。systemd 那边会先 stop 再 disable,然后 `daemon-reload` + `reset-failed` 把状态清干净。
|
直接删掉:内核二进制、`mihomo.service` 及其备份、`mihomo.service.d/` drop-in 目录、快捷命令和它的缓存。systemd 那边会先 stop 再 disable,然后 `daemon-reload` + `reset-failed` 把状态清干净。
|
||||||
|
|
||||||
会单独问你的两项:配置目录 `/etc/mihomo`(订阅、节点选择记录、面板文件都在里面)、内核备份目录 `/var/backups/mihomo`。留着的话下次重装能直接接上。
|
会单独问你的两项:配置目录 `/etc/mihomo`(订阅、节点选择记录、面板文件都在里面)、内核备份目录 `/var/backups/mihomo`。留着的话下次重新安装能直接接上。
|
||||||
|
|
||||||
## 在 cron / CI 里用
|
## 在 cron / CI 里用
|
||||||
|
|
||||||
无参数运行时,脚本会看 stdin 是不是终端:是就进菜单,被管道或 cron 调用就安静地跑 `check`。
|
无参数运行时,脚本会看 stdin 是不是终端:是就进菜单,被管道或 cron 调用就安静地运行 `check`。
|
||||||
|
|
||||||
`check` 的退出码专为脚本化设计:
|
`check` 的退出码专为脚本化设计:
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ mihomo-up.sh check && exit 0
|
|||||||
sudo ./mihomo-up.sh install --mirror https://ghfast.top/
|
sudo ./mihomo-up.sh install --mirror https://ghfast.top/
|
||||||
```
|
```
|
||||||
|
|
||||||
带 `--mirror` 跑配置向导时,生成的配置里会额外写上走同一个镜像的 `geox-url`,免得内核首次启动下载 GeoIP 数据库时卡住。
|
带 `--mirror` 运行配置向导时,生成的配置里会额外写上走同一个镜像的 `geox-url`,免得内核首次启动下载 GeoIP 数据库时卡住。
|
||||||
|
|
||||||
## 选项一览
|
## 选项一览
|
||||||
|
|
||||||
@@ -185,10 +185,10 @@ sudo ./mihomo-up.sh install --mirror https://ghfast.top/
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `-y, --yes` | 非交互确认,所有询问都当作同意 |
|
| `-y, --yes` | 非交互确认,所有询问都当作同意 |
|
||||||
| `-n, --dry-run` | 只打印会做什么,不实际改动 |
|
| `-n, --dry-run` | 只打印会做什么,不实际改动 |
|
||||||
| `--force` | 版本相同也重装 |
|
| `--force` | 版本相同也重新安装 |
|
||||||
| `--version <v>` | 指定内核版本,如 `v1.19.28` |
|
| `--version <v>` | 指定内核版本,如 `v1.19.28` |
|
||||||
| `--no-restart` | 装完不启动服务 |
|
| `--no-restart` | 安装完成后不启动服务 |
|
||||||
| `--purge` | `uninstall` 时连配置目录和备份一起删 |
|
| `--purge` | `uninstall` 时连配置目录和备份一起删除 |
|
||||||
| `--skip-conf-check` | 升级时不用新版本预校验现有配置 |
|
| `--skip-conf-check` | 升级时不用新版本预校验现有配置 |
|
||||||
| `--json` | `check` 时额外输出一行 JSON |
|
| `--json` | `check` 时额外输出一行 JSON |
|
||||||
| `--arch <a>` | 覆盖自动识别的架构:`amd64` / `arm64` / `armv7` |
|
| `--arch <a>` | 覆盖自动识别的架构:`amd64` / `arm64` / `armv7` |
|
||||||
@@ -215,7 +215,7 @@ sudo ./mihomo-up.sh install --mirror https://ghfast.top/
|
|||||||
| `/var/lib/mihomo-up`(脚本缓存) | `MIHOMO_UP_CACHE` |
|
| `/var/lib/mihomo-up`(脚本缓存) | `MIHOMO_UP_CACHE` |
|
||||||
| 仓库 raw 地址(快捷命令拉取源) | `MIHOMO_UP_URL` |
|
| 仓库 raw 地址(快捷命令拉取源) | `MIHOMO_UP_URL` |
|
||||||
|
|
||||||
内核备份最多保留 3 份(脚本内常量 `KEEP=3`)。配置备份不自动清理,攒多了自己删 `config.yaml.bak-*`。
|
内核备份最多保留 3 份(脚本内常量 `KEEP=3`)。配置备份不自动清理,积累多了自己删除 `config.yaml.bak-*`。
|
||||||
|
|
||||||
## 参考
|
## 参考
|
||||||
|
|
||||||
|
|||||||
+58
-58
@@ -8,7 +8,7 @@
|
|||||||
# 3. 配置文件 默认 /etc/mihomo/config.yaml
|
# 3. 配置文件 默认 /etc/mihomo/config.yaml
|
||||||
#
|
#
|
||||||
# bash <(curl -fsSL https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.sh)
|
# bash <(curl -fsSL https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.sh)
|
||||||
# └─ 交互菜单;一键安装、改配置、升级、卸载都在里面
|
# └─ 交互菜单;一键安装、修改配置、升级、卸载都在里面
|
||||||
# ./mihomo-up.sh check # 非交互,给 cron/CI 用,退出码见 --help
|
# ./mihomo-up.sh check # 非交互,给 cron/CI 用,退出码见 --help
|
||||||
#
|
#
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
@@ -25,8 +25,8 @@ BACKUP_DIR="${MIHOMO_BACKUP_DIR:-/var/backups/mihomo}"
|
|||||||
UNIT_FILE="/etc/systemd/system/${SERVICE}.service"
|
UNIT_FILE="/etc/systemd/system/${SERVICE}.service"
|
||||||
KEEP=3
|
KEEP=3
|
||||||
|
|
||||||
# 快捷命令: 装一个 mihomo-up 到 PATH 里,省得每次都记那条 curl 长链接。
|
# 快捷命令: 安装一个 mihomo-up 到 PATH 里,省得每次都记那条 curl 长链接。
|
||||||
# 它是个启动器,每次执行先拉最新脚本,拉不到就回落到 CACHE_DIR 里的缓存。
|
# 它是个启动器,每次执行先拉取最新脚本,拉取失败就回落到 CACHE_DIR 里的缓存。
|
||||||
SCRIPT_URL="${MIHOMO_UP_URL:-https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.sh}"
|
SCRIPT_URL="${MIHOMO_UP_URL:-https://git.im0o.top/im0o/mihomo-up/raw/branch/main/mihomo-up.sh}"
|
||||||
LAUNCHER="${MIHOMO_UP_LAUNCHER:-/usr/local/bin/mihomo-up}"
|
LAUNCHER="${MIHOMO_UP_LAUNCHER:-/usr/local/bin/mihomo-up}"
|
||||||
CACHE_DIR="${MIHOMO_UP_CACHE:-/var/lib/mihomo-up}"
|
CACHE_DIR="${MIHOMO_UP_CACHE:-/var/lib/mihomo-up}"
|
||||||
@@ -66,22 +66,22 @@ usage() {
|
|||||||
mihomo-up.sh — 安装 / 配置 / 升级 / 卸载 mihomo 内核
|
mihomo-up.sh — 安装 / 配置 / 升级 / 卸载 mihomo 内核
|
||||||
|
|
||||||
mihomo-up.sh 进入交互菜单(终端下默认)
|
mihomo-up.sh 进入交互菜单(终端下默认)
|
||||||
mihomo-up.sh install 一键安装:装内核 + 写 systemd + 生成配置 + 启动
|
mihomo-up.sh install 一键安装:内核 + systemd 服务 + 配置向导 + 启动
|
||||||
mihomo-up.sh check 只查,不改任何东西
|
mihomo-up.sh check 只查询,不改动任何东西
|
||||||
mihomo-up.sh update [--force] 只换内核二进制,不碰配置和服务定义
|
mihomo-up.sh update [--force] 只更换内核二进制,不碰配置和服务定义
|
||||||
mihomo-up.sh config 进入配置向导,重新生成 config.yaml
|
mihomo-up.sh config 进入配置向导,重新生成 config.yaml
|
||||||
mihomo-up.sh service 只写入/刷新 systemd 服务文件
|
mihomo-up.sh service 只写入 / 刷新 systemd 服务文件
|
||||||
mihomo-up.sh shortcut 只装/刷新 mihomo-up 快捷命令
|
mihomo-up.sh shortcut 只安装 / 刷新 mihomo-up 快捷命令
|
||||||
mihomo-up.sh rollback 回滚到上一个备份的内核
|
mihomo-up.sh rollback 回滚到上一个备份的内核
|
||||||
mihomo-up.sh list 列出本地内核备份
|
mihomo-up.sh list 列出本地内核备份
|
||||||
mihomo-up.sh uninstall [--purge] 卸载内核和服务;--purge 连配置和备份一起删
|
mihomo-up.sh uninstall [--purge] 卸载内核和服务;--purge 连配置和备份一起删除
|
||||||
|
|
||||||
选项:
|
选项:
|
||||||
-y, --yes 非交互确认,所有询问都当作同意
|
-y, --yes 非交互确认,所有询问都当作同意
|
||||||
-n, --dry-run 只打印会做什么,不实际改动
|
-n, --dry-run 只打印会做什么,不实际改动
|
||||||
--force 版本相同也重装
|
--force 版本相同也重新安装
|
||||||
--version <v> 指定内核版本,如 v1.19.28
|
--version <v> 指定内核版本,如 v1.19.28
|
||||||
--no-restart 装完不启动服务
|
--no-restart 安装完成后不启动服务
|
||||||
--purge uninstall 时连配置目录和备份一起删除
|
--purge uninstall 时连配置目录和备份一起删除
|
||||||
--skip-conf-check 升级时不用新版本预校验现有配置(配置本来就有问题时用)
|
--skip-conf-check 升级时不用新版本预校验现有配置(配置本来就有问题时用)
|
||||||
--json check 时额外输出一行 JSON
|
--json check 时额外输出一行 JSON
|
||||||
@@ -138,7 +138,7 @@ pause() { local _x; ask "$(printf '\n%s按回车继续…%s' "$D" "$N")" _x; }
|
|||||||
need() { command -v "$1" >/dev/null 2>&1 || die "缺少命令: $1"; }
|
need() { command -v "$1" >/dev/null 2>&1 || die "缺少命令: $1"; }
|
||||||
|
|
||||||
require_root() {
|
require_root() {
|
||||||
[[ ${EUID:-$(id -u)} -eq 0 ]] || die "$1 需要 root 权限,请用 sudo 重跑"
|
[[ ${EUID:-$(id -u)} -eq 0 ]] || die "$1 需要 root 权限,请用 sudo 重新运行"
|
||||||
}
|
}
|
||||||
|
|
||||||
has_systemd() { command -v systemctl >/dev/null 2>&1; }
|
has_systemd() { command -v systemctl >/dev/null 2>&1; }
|
||||||
@@ -219,7 +219,7 @@ stage_binary() { # $1 = 目标版本;成功后 STAGED 指
|
|||||||
gzip -dc "$TMPDIR_/m.gz" > "$TMPDIR_/mihomo" || die "解压失败,下载到的不是有效的 .gz 文件"
|
gzip -dc "$TMPDIR_/m.gz" > "$TMPDIR_/mihomo" || die "解压失败,下载到的不是有效的 .gz 文件"
|
||||||
chmod +x "$TMPDIR_/mihomo"
|
chmod +x "$TMPDIR_/mihomo"
|
||||||
|
|
||||||
# 闸门 1: 新二进制能跑起来,且自报版本号和目标一致
|
# 闸门 1: 新二进制能运行起来,且自报版本号和目标一致
|
||||||
local line ver
|
local line ver
|
||||||
line="$(version_line "$TMPDIR_/mihomo")"
|
line="$(version_line "$TMPDIR_/mihomo")"
|
||||||
[[ -n "$line" ]] || die "新二进制执行不了,多半是架构选错了(这次按 $(detect_arch) 下载,可用 --arch 覆盖)"
|
[[ -n "$line" ]] || die "新二进制执行不了,多半是架构选错了(这次按 $(detect_arch) 下载,可用 --arch 覆盖)"
|
||||||
@@ -249,14 +249,14 @@ do_update() { # $1 = 目标版本;只换二进制,不
|
|||||||
|
|
||||||
stage_binary "$target"
|
stage_binary "$target"
|
||||||
|
|
||||||
# 闸门 2: 用新版本校验现有配置,配置在新版本下跑不通就别换
|
# 闸门 2: 用新版本校验现有配置。现有配置在新内核上通不过,就不要替换
|
||||||
if [[ -f "$(conf_file)" ]] && (( SKIP_CONF_CHECK == 0 )); then
|
if [[ -f "$(conf_file)" ]] && (( SKIP_CONF_CHECK == 0 )); then
|
||||||
local out
|
local out
|
||||||
if out="$(test_conf_with "$STAGED")"; then
|
if out="$(test_conf_with "$STAGED")"; then
|
||||||
ok "现有配置在 $target 下校验通过"
|
ok "现有配置在 $target 下校验通过"
|
||||||
else
|
else
|
||||||
warn "$target 校验现有配置失败:"; sed 's/^/ /' <<<"$out" >&2
|
warn "$target 校验现有配置失败:"; sed 's/^/ /' <<<"$out" >&2
|
||||||
die "没有替换任何文件。先按上面的报错改配置(改完用 $BIN -t -d $CONF_DIR 复验),或加 --skip-conf-check 只换内核不管配置"
|
die "没有替换任何文件。先按上面的报错修改配置(改完用 $BIN -t -d $CONF_DIR 重新校验),或加 --skip-conf-check 只更换内核不管配置"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -379,21 +379,21 @@ start_service() {
|
|||||||
if [[ "$(svc_state)" == "running" ]]; then
|
if [[ "$(svc_state)" == "running" ]]; then
|
||||||
ok "$SERVICE 已启动"
|
ok "$SERVICE 已启动"
|
||||||
else
|
else
|
||||||
warn "$SERVICE 没起来。看具体原因: journalctl -u $SERVICE -n 50 --no-pager"
|
warn "$SERVICE 没有启动成功。看具体原因: journalctl -u $SERVICE -n 50 --no-pager"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ───────────────────────── 快捷命令 ─────────────────────────
|
# ───────────────────────── 快捷命令 ─────────────────────────
|
||||||
# 设计取舍: 启动器每次跑都拉最新脚本,所以你改了仓库、客户那边下次执行就用上了,
|
# 设计取舍: 启动器每次运行都拉取最新脚本,所以你改了仓库、客户那边下次执行就用上了,
|
||||||
# 不需要谁去手动更新。但它落一份缓存并在拉取失败时回落 —— 这个脚本管的是代理,
|
# 不需要谁去手动更新。但它落一份缓存并在拉取失败时回落 —— 这个脚本管的是代理,
|
||||||
# 你最需要它的时候常常正是网络不正常的时候,那时候必须还能进菜单重启服务、看日志。
|
# 你最需要它的时候常常正是网络不正常的时候,那时候必须还能进菜单重启服务、看日志。
|
||||||
write_launcher() {
|
write_launcher() {
|
||||||
local out="$1"
|
local out="$1"
|
||||||
{
|
{
|
||||||
printf '#!/usr/bin/env bash\n'
|
printf '#!/usr/bin/env bash\n'
|
||||||
printf '# mihomo-up 启动器 — 由 mihomo-up.sh v%s 生成,别手动改,重装会覆盖\n' "$SELF_VERSION"
|
printf '# mihomo-up 启动器 — 由 mihomo-up.sh v%s 生成,别手动改,重新安装会覆盖\n' "$SELF_VERSION"
|
||||||
printf '# 先拉最新脚本跑;拉不到 (网络断了 / 仓库挂了) 就用本地缓存。\n'
|
printf '# 先拉取最新脚本运行;拉取失败 (网络断了 / 仓库挂了) 就用本地缓存。\n'
|
||||||
printf 'SRC="${MIHOMO_UP_URL:-%s}"\n' "$SCRIPT_URL"
|
printf 'SRC="${MIHOMO_UP_URL:-%s}"\n' "$SCRIPT_URL"
|
||||||
printf 'CACHE="%s/mihomo-up.sh"\n' "$CACHE_DIR"
|
printf 'CACHE="%s/mihomo-up.sh"\n' "$CACHE_DIR"
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
@@ -402,7 +402,7 @@ RUN=""
|
|||||||
|
|
||||||
# --connect-timeout / --max-time: 网络卡住时别让菜单一直转,那正是要用它的时候
|
# --connect-timeout / --max-time: 网络卡住时别让菜单一直转,那正是要用它的时候
|
||||||
if curl -fsSL --connect-timeout 5 --max-time 20 "$SRC" -o "$TMP" && [[ -s "$TMP" ]]; then
|
if curl -fsSL --connect-timeout 5 --max-time 20 "$SRC" -o "$TMP" && [[ -s "$TMP" ]]; then
|
||||||
# 拿到了不等于能跑: 上游推坏了、或者中间设备塞回来一个门户登录页,
|
# 拿到了不等于能运行: 上游推坏了、或者中间设备塞回来一个门户登录页,
|
||||||
# 都不该以 root 执行。语法过不了就当这次没拉到, 回落缓存。
|
# 都不该以 root 执行。语法过不了就当这次没拉到, 回落缓存。
|
||||||
if bash -n "$TMP" 2>/dev/null; then
|
if bash -n "$TMP" 2>/dev/null; then
|
||||||
mkdir -p "${CACHE%/*}" 2>/dev/null || true
|
mkdir -p "${CACHE%/*}" 2>/dev/null || true
|
||||||
@@ -418,17 +418,17 @@ if [[ -z "$RUN" && -x "$CACHE" ]]; then
|
|||||||
printf ' 改用本地缓存 (%s)\n' "$(date -r "$CACHE" '+%F %T')" >&2
|
printf ' 改用本地缓存 (%s)\n' "$(date -r "$CACHE" '+%F %T')" >&2
|
||||||
RUN="$CACHE"
|
RUN="$CACHE"
|
||||||
fi
|
fi
|
||||||
[[ -n "$RUN" ]] || { printf '✗ 远端拉不到,本地也没有缓存,没得跑了\n' >&2; exit 1; }
|
[[ -n "$RUN" ]] || { printf '✗ 远端拉取失败,本地也没有缓存,没有可执行的脚本\n' >&2; exit 1; }
|
||||||
|
|
||||||
bash "$RUN" "$@"
|
bash "$RUN" "$@"
|
||||||
EOF
|
EOF
|
||||||
} > "$out"
|
} > "$out"
|
||||||
}
|
}
|
||||||
|
|
||||||
seed_cache() { # 先放一份缓存,这样装完立刻断网也能用
|
seed_cache() { # 先放一份缓存,这样安装完成后立刻断网也能用
|
||||||
local cache="$CACHE_DIR/mihomo-up.sh"
|
local cache="$CACHE_DIR/mihomo-up.sh"
|
||||||
mkdir -p "$CACHE_DIR"
|
mkdir -p "$CACHE_DIR"
|
||||||
# bash <(curl ...) 跑的时候 $0 是条管道,内容已被 bash 读掉一截,复制不出完整脚本,
|
# bash <(curl ...) 运行的时候 $0 是条管道,内容已被 bash 读掉一截,复制不出完整脚本,
|
||||||
# 只有 $0 是真实文件时才能直接拷,否则老老实实重新下一份。
|
# 只有 $0 是真实文件时才能直接拷,否则老老实实重新下一份。
|
||||||
if [[ -f "$0" && "$0" != /dev/fd/* && "$0" != /proc/* ]]; then
|
if [[ -f "$0" && "$0" != /dev/fd/* && "$0" != /proc/* ]]; then
|
||||||
install -m 0755 "$0" "$cache"; return 0
|
install -m 0755 "$0" "$cache"; return 0
|
||||||
@@ -440,7 +440,7 @@ seed_cache() { # 先放一份缓存,这样装完立刻断
|
|||||||
fi
|
fi
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
warn "缓存没建立: 从 $SCRIPT_URL 下载失败"
|
warn "缓存没建立: 从 $SCRIPT_URL 下载失败"
|
||||||
warn "快捷命令照样能用,但这台机器断网时它既拉不到脚本、也没有缓存可回落,会直接报错"
|
warn "快捷命令照样能用,但这台机器断网时它既拉取不到脚本、也没有缓存可回落,会直接报错"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,7 +453,7 @@ install_launcher() {
|
|||||||
bash -n "$tmp" || { rm -f "$tmp"; die "生成的启动器语法有问题,没有安装(这是脚本自身的 bug,请反馈)"; }
|
bash -n "$tmp" || { rm -f "$tmp"; die "生成的启动器语法有问题,没有安装(这是脚本自身的 bug,请反馈)"; }
|
||||||
install -m 0755 "$tmp" "$LAUNCHER"; rm -f "$tmp"
|
install -m 0755 "$tmp" "$LAUNCHER"; rm -f "$tmp"
|
||||||
seed_cache || true
|
seed_cache || true
|
||||||
ok "快捷命令已装好: 以后直接敲 ${W}$(basename "$LAUNCHER")${N} 就能进菜单"
|
ok "快捷命令已安装好: 以后直接输入 ${W}$(basename "$LAUNCHER")${N} 就能进菜单"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ───────────────────────── 配置 ─────────────────────────
|
# ───────────────────────── 配置 ─────────────────────────
|
||||||
@@ -477,7 +477,7 @@ cfg_restore() { # cfg_restore <备份路径>
|
|||||||
|
|
||||||
cfg_require() { # 没有配置文件时,给一句能照着做的提示
|
cfg_require() { # 没有配置文件时,给一句能照着做的提示
|
||||||
[[ -f "$(conf_file)" ]] && return 0
|
[[ -f "$(conf_file)" ]] && return 0
|
||||||
warn "还没有配置文件 $(conf_file),先跑一遍配置向导生成(菜单「配置管理 → 生成配置」,命令行是 $0 config)"
|
warn "还没有配置文件 $(conf_file),先运行一遍配置向导生成(菜单「配置管理 → 生成配置」,命令行是 $0 config)"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ cfg_set() { # cfg_set <顶层键> <值>
|
|||||||
cfg_validate() { # 校验配置;无效时打印错误并返回 1
|
cfg_validate() { # 校验配置;无效时打印错误并返回 1
|
||||||
local f; f="$(conf_file)"
|
local f; f="$(conf_file)"
|
||||||
[[ -f "$f" ]] || { warn "还没有配置文件 $f"; return 1; }
|
[[ -f "$f" ]] || { warn "还没有配置文件 $f"; return 1; }
|
||||||
[[ -x "$BIN" ]] || { warn "内核还没装($BIN 不存在),没法校验,先「一键安装」"; return 1; }
|
[[ -x "$BIN" ]] || { warn "内核还没安装($BIN 不存在),无法校验,先「一键安装」"; return 1; }
|
||||||
local out
|
local out
|
||||||
if out="$("$BIN" -t -d "$CONF_DIR" 2>&1)"; then
|
if out="$("$BIN" -t -d "$CONF_DIR" 2>&1)"; then
|
||||||
ok "配置有效"; return 0
|
ok "配置有效"; return 0
|
||||||
@@ -546,7 +546,7 @@ cfg_wizard() { # 生成一份全新的 config.yaml
|
|||||||
warn " 一直没拿到订阅链接,向导先退出了,配置没有改动"
|
warn " 一直没拿到订阅链接,向导先退出了,配置没有改动"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
warn " 订阅链接不能空,没有节点 mihomo 起不来"
|
warn " 订阅链接不能空,没有节点 mihomo 启动不了"
|
||||||
done
|
done
|
||||||
|
|
||||||
ask_default " 本地代理端口(HTTP 和 SOCKS5 共用)" "${old_port:-7890}" port
|
ask_default " 本地代理端口(HTTP 和 SOCKS5 共用)" "${old_port:-7890}" port
|
||||||
@@ -556,7 +556,7 @@ cfg_wizard() { # 生成一份全新的 config.yaml
|
|||||||
|
|
||||||
log ""
|
log ""
|
||||||
log " ${D}管理接口用来接 Web 面板和 API。开在 0.0.0.0 等于对外网暴露,务必设密钥。${N}"
|
log " ${D}管理接口用来接 Web 面板和 API。开在 0.0.0.0 等于对外网暴露,务必设密钥。${N}"
|
||||||
ask_default " 管理接口: 1=只允许本机 2=局域网/公网可访问 3=不开" "1" ctrl_mode
|
ask_default " 管理接口: 1=只允许本机 2=局域网/公网可访问 3=不开启" "1" ctrl_mode
|
||||||
case "$ctrl_mode" in
|
case "$ctrl_mode" in
|
||||||
2) local cport; ask_default " 管理接口端口" "9090" cport; ctrl="0.0.0.0:${cport}" ;;
|
2) local cport; ask_default " 管理接口端口" "9090" cport; ctrl="0.0.0.0:${cport}" ;;
|
||||||
3) ctrl="" ;;
|
3) ctrl="" ;;
|
||||||
@@ -566,7 +566,7 @@ cfg_wizard() { # 生成一份全新的 config.yaml
|
|||||||
secret=""
|
secret=""
|
||||||
if [[ -n "$ctrl" ]]; then
|
if [[ -n "$ctrl" ]]; then
|
||||||
ask_default " 管理接口密钥(回车=自动生成一个)" "${old_secret:-$(gen_secret)}" secret
|
ask_default " 管理接口密钥(回车=自动生成一个)" "${old_secret:-$(gen_secret)}" secret
|
||||||
if confirm_default_yes " 顺带装个 Web 面板(metacubexd,内核启动时自动下载)?"; then ui="yes"; else ui="no"; fi
|
if confirm_default_yes " 顺带安装一个 Web 面板(metacubexd,内核启动时自动下载)?"; then ui="yes"; else ui="no"; fi
|
||||||
else
|
else
|
||||||
ui="no"
|
ui="no"
|
||||||
fi
|
fi
|
||||||
@@ -631,7 +631,7 @@ proxy-providers:
|
|||||||
type: http
|
type: http
|
||||||
url: "${sub}" # mihomo-up:sub
|
url: "${sub}" # mihomo-up:sub
|
||||||
path: ./providers/subs.yaml
|
path: ./providers/subs.yaml
|
||||||
interval: 86400 # 每 24 小时自动拉一次订阅
|
interval: 86400 # 每 24 小时自动拉取一次订阅
|
||||||
health-check:
|
health-check:
|
||||||
enable: true
|
enable: true
|
||||||
url: https://www.gstatic.com/generate_204
|
url: https://www.gstatic.com/generate_204
|
||||||
@@ -676,7 +676,7 @@ EOF
|
|||||||
install -m 0644 "$tmp" "$f"; rm -f "$tmp"
|
install -m 0644 "$tmp" "$f"; rm -f "$tmp"
|
||||||
ok "配置已写入 $f"
|
ok "配置已写入 $f"
|
||||||
|
|
||||||
cfg_apply "$bak" || warn "配置写进去了但校验没过,可以用「编辑配置」改,或重新跑一遍向导"
|
cfg_apply "$bak" || warn "配置写进去了但校验没过,可以用「编辑配置」改,或重新运行一遍向导"
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_sub_url() { # 读回本脚本写的订阅链接
|
cfg_sub_url() { # 读回本脚本写的订阅链接
|
||||||
@@ -688,11 +688,11 @@ cfg_sub_url() { # 读回本脚本写的订阅链接
|
|||||||
|
|
||||||
cfg_change_sub() {
|
cfg_change_sub() {
|
||||||
local f; f="$(conf_file)"
|
local f; f="$(conf_file)"
|
||||||
[[ -f "$f" ]] || { warn "还没有配置文件,先用「生成配置」跑一遍向导"; return 1; }
|
[[ -f "$f" ]] || { warn "还没有配置文件,先用「生成配置」运行一遍向导"; return 1; }
|
||||||
local cur; cur="$(cfg_sub_url)"
|
local cur; cur="$(cfg_sub_url)"
|
||||||
if [[ -z "$cur" ]]; then
|
if [[ -z "$cur" ]]; then
|
||||||
warn "在 $f 里没找到本脚本写的订阅行(标记是行尾的 # mihomo-up:sub)"
|
warn "在 $f 里没找到本脚本写的订阅行(标记是行尾的 # mihomo-up:sub)"
|
||||||
warn "这份配置多半是手写或外部导入的,改订阅请用「编辑配置」,或重跑向导生成新配置"
|
warn "这份配置多半是手写或外部导入的,修改订阅请用「编辑配置」,或重新运行向导生成新配置"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
local new; ask_default " 新的订阅链接" "$cur" new
|
local new; ask_default " 新的订阅链接" "$cur" new
|
||||||
@@ -738,7 +738,7 @@ cfg_change_ctrl() {
|
|||||||
local cur_ctrl cur_secret
|
local cur_ctrl cur_secret
|
||||||
cur_ctrl="$(cfg_get 'external-controller')"; cur_secret="$(cfg_get 'secret')"
|
cur_ctrl="$(cfg_get 'external-controller')"; cur_secret="$(cfg_get 'secret')"
|
||||||
log " ${D}当前: ${cur_ctrl:-未开启} 密钥: ${cur_secret:-无}${N}"
|
log " ${D}当前: ${cur_ctrl:-未开启} 密钥: ${cur_secret:-无}${N}"
|
||||||
local mode; ask_default " 管理接口: 1=只允许本机 2=局域网/公网可访问 3=不开" "1" mode
|
local mode; ask_default " 管理接口: 1=只允许本机 2=局域网/公网可访问 3=不开启" "1" mode
|
||||||
local bak; bak="$(cfg_backup)"
|
local bak; bak="$(cfg_backup)"
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
3) cfg_set 'external-controller' '""'; ok "已关闭管理接口(面板也就连不上了)" ;;
|
3) cfg_set 'external-controller' '""'; ok "已关闭管理接口(面板也就连不上了)" ;;
|
||||||
@@ -774,7 +774,7 @@ cfg_import_url() {
|
|||||||
|
|
||||||
cfg_edit() {
|
cfg_edit() {
|
||||||
local f; f="$(conf_file)"
|
local f; f="$(conf_file)"
|
||||||
[[ -f "$f" ]] || { warn "还没有配置文件,先用「生成配置」跑一遍向导"; return 1; }
|
[[ -f "$f" ]] || { warn "还没有配置文件,先用「生成配置」运行一遍向导"; return 1; }
|
||||||
local ed="${EDITOR:-}"
|
local ed="${EDITOR:-}"
|
||||||
[[ -n "$ed" ]] || { command -v nano >/dev/null 2>&1 && ed=nano || ed=vi; }
|
[[ -n "$ed" ]] || { command -v nano >/dev/null 2>&1 && ed=nano || ed=vi; }
|
||||||
command -v "${ed%% *}" >/dev/null 2>&1 || { warn "找不到编辑器 $ed,可以先 export EDITOR=你的编辑器 再回来"; return 1; }
|
command -v "${ed%% *}" >/dev/null 2>&1 || { warn "找不到编辑器 $ed,可以先 export EDITOR=你的编辑器 再回来"; return 1; }
|
||||||
@@ -816,7 +816,7 @@ do_install() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
log "${W} 一键安装${N} ${D}— 依次做五件事:装内核、准备配置、写 systemd 服务、装快捷命令、启动${N}"
|
log "${W} 一键安装${N} ${D}— 依次做五件事:安装内核、准备配置、写入 systemd 服务、安装快捷命令、启动${N}"
|
||||||
log " ${D}内核 ${N}$BIN"
|
log " ${D}内核 ${N}$BIN"
|
||||||
log " ${D}配置 ${N}$(conf_file)"
|
log " ${D}配置 ${N}$(conf_file)"
|
||||||
log " ${D}服务 ${N}${UNIT_FILE}"
|
log " ${D}服务 ${N}${UNIT_FILE}"
|
||||||
@@ -832,7 +832,7 @@ do_install() {
|
|||||||
info "内核已是 $cur,跳过下载"
|
info "内核已是 $cur,跳过下载"
|
||||||
else
|
else
|
||||||
stage_binary "$target"
|
stage_binary "$target"
|
||||||
[[ "$(svc_state)" == "running" ]] && { info "先停掉正在跑的 $SERVICE"; systemctl stop "$SERVICE" || true; }
|
[[ "$(svc_state)" == "running" ]] && { info "先停掉正在运行的 $SERVICE"; systemctl stop "$SERVICE" || true; }
|
||||||
if [[ -x "$BIN" && -n "$cur" ]]; then
|
if [[ -x "$BIN" && -n "$cur" ]]; then
|
||||||
mkdir -p "$BACKUP_DIR"; cp -a "$BIN" "$BACKUP_DIR/mihomo-$cur"
|
mkdir -p "$BACKUP_DIR"; cp -a "$BIN" "$BACKUP_DIR/mihomo-$cur"
|
||||||
info "旧内核已备份 → $BACKUP_DIR/mihomo-$cur"
|
info "旧内核已备份 → $BACKUP_DIR/mihomo-$cur"
|
||||||
@@ -850,7 +850,7 @@ do_install() {
|
|||||||
confirm " 要用向导重新生成一份吗?" && { cfg_wizard || true; }
|
confirm " 要用向导重新生成一份吗?" && { cfg_wizard || true; }
|
||||||
else
|
else
|
||||||
log ""
|
log ""
|
||||||
warn "还没有配置文件,没有它 mihomo 起不来"
|
warn "还没有配置文件,没有它 mihomo 启动不了"
|
||||||
if confirm_default_yes " 现在用向导生成一份?"; then
|
if confirm_default_yes " 现在用向导生成一份?"; then
|
||||||
cfg_wizard && has_conf=1 || has_conf=0
|
cfg_wizard && has_conf=1 || has_conf=0
|
||||||
else
|
else
|
||||||
@@ -864,7 +864,7 @@ do_install() {
|
|||||||
# 4. 快捷命令
|
# 4. 快捷命令
|
||||||
if [[ -x "$LAUNCHER" ]]; then
|
if [[ -x "$LAUNCHER" ]]; then
|
||||||
install_launcher # 已经有了就顺手刷新成当前版本
|
install_launcher # 已经有了就顺手刷新成当前版本
|
||||||
elif confirm_default_yes " 装个 $(basename "$LAUNCHER") 快捷命令,省得每次记 curl 地址?"; then
|
elif confirm_default_yes " 安装一个 $(basename "$LAUNCHER") 快捷命令,省得每次记 curl 地址?"; then
|
||||||
install_launcher
|
install_launcher
|
||||||
else
|
else
|
||||||
info "跳过。下次还用那条 bash <(curl ...) 进菜单"
|
info "跳过。下次还用那条 bash <(curl ...) 进菜单"
|
||||||
@@ -888,7 +888,7 @@ print_summary() {
|
|||||||
secret="$(cfg_get 'secret')"; ui="$(cfg_get 'external-ui')"
|
secret="$(cfg_get 'secret')"; ui="$(cfg_get 'external-ui')"
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
log "${G} ── 装好了 ─────────────────────────────${N}"
|
log "${G} ── 安装完成 ─────────────────────────────${N}"
|
||||||
[[ -n "$port" ]] && {
|
[[ -n "$port" ]] && {
|
||||||
log " 本机代理 ${W}http://127.0.0.1:${port}${N} ${D}(HTTP 和 SOCKS5 同一个端口)${N}"
|
log " 本机代理 ${W}http://127.0.0.1:${port}${N} ${D}(HTTP 和 SOCKS5 同一个端口)${N}"
|
||||||
log " 临时用起来 ${D}export https_proxy=http://127.0.0.1:${port} http_proxy=http://127.0.0.1:${port}${N}"
|
log " 临时用起来 ${D}export https_proxy=http://127.0.0.1:${port} http_proxy=http://127.0.0.1:${port}${N}"
|
||||||
@@ -905,7 +905,7 @@ print_summary() {
|
|||||||
log " 服务状态 $(systemctl is-active "$SERVICE" 2>/dev/null || echo '未运行') ${D}开机自启: $(svc_enabled)${N}"
|
log " 服务状态 $(systemctl is-active "$SERVICE" 2>/dev/null || echo '未运行') ${D}开机自启: $(svc_enabled)${N}"
|
||||||
log " 看日志 ${D}journalctl -u $SERVICE -f${N}"
|
log " 看日志 ${D}journalctl -u $SERVICE -f${N}"
|
||||||
[[ -x "$LAUNCHER" ]] && \
|
[[ -x "$LAUNCHER" ]] && \
|
||||||
log " 再进菜单 ${W}$(basename "$LAUNCHER")${N} ${D}(每次自动拉最新脚本,拉不到就用本地缓存)${N}"
|
log " 再进菜单 ${W}$(basename "$LAUNCHER")${N} ${D}(每次自动拉取最新脚本,拉取失败就用本地缓存)${N}"
|
||||||
log "${G} ───────────────────────────────────────${N}"
|
log "${G} ───────────────────────────────────────${N}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,21 +938,21 @@ do_uninstall() {
|
|||||||
|
|
||||||
if [[ -e "$BIN" ]]; then rm -f "$BIN"; ok "已删除 $BIN"; fi
|
if [[ -e "$BIN" ]]; then rm -f "$BIN"; ok "已删除 $BIN"; fi
|
||||||
|
|
||||||
# 快捷命令和它的缓存。注意: 你很可能正是从缓存里那份脚本跑起来的,删掉没关系 ——
|
# 快捷命令和它的缓存。注意: 你很可能正是从缓存里那份脚本启动起来的,删掉没关系 ——
|
||||||
# Linux 上文件被删除后 inode 还在,bash 手里的 fd 仍然有效,这一趟能跑完。
|
# Linux 上文件被删除后 inode 还在,bash 手里的 fd 仍然有效,这一趟能运行完。
|
||||||
[[ -e "$LAUNCHER" ]] && { rm -f "$LAUNCHER"; ok "已删除快捷命令 $LAUNCHER"; }
|
[[ -e "$LAUNCHER" ]] && { rm -f "$LAUNCHER"; ok "已删除快捷命令 $LAUNCHER"; }
|
||||||
[[ -d "$CACHE_DIR" ]] && { rm -rf "$CACHE_DIR"; ok "已删除脚本缓存 $CACHE_DIR"; }
|
[[ -d "$CACHE_DIR" ]] && { rm -rf "$CACHE_DIR"; ok "已删除脚本缓存 $CACHE_DIR"; }
|
||||||
|
|
||||||
if [[ -d "$CONF_DIR" ]]; then
|
if [[ -d "$CONF_DIR" ]]; then
|
||||||
if (( PURGE )) || confirm " 连配置目录 $CONF_DIR 一起删?(订阅、节点选择、面板文件都在里面)"; then
|
if (( PURGE )) || confirm " 连配置目录 $CONF_DIR 一起删除?(订阅、节点选择、面板文件都在里面)"; then
|
||||||
rm -rf "$CONF_DIR"; ok "已删除 $CONF_DIR"
|
rm -rf "$CONF_DIR"; ok "已删除 $CONF_DIR"
|
||||||
else
|
else
|
||||||
info "配置保留在 $CONF_DIR,下次重装能直接接上"
|
info "配置保留在 $CONF_DIR,下次重新安装能直接接上"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -d "$BACKUP_DIR" ]]; then
|
if [[ -d "$BACKUP_DIR" ]]; then
|
||||||
if (( PURGE )) || confirm " 连内核备份 $BACKUP_DIR 一起删?"; then
|
if (( PURGE )) || confirm " 连内核备份 $BACKUP_DIR 一起删除?"; then
|
||||||
rm -rf "$BACKUP_DIR"; ok "已删除 $BACKUP_DIR"
|
rm -rf "$BACKUP_DIR"; ok "已删除 $BACKUP_DIR"
|
||||||
else
|
else
|
||||||
info "内核备份保留在 $BACKUP_DIR"
|
info "内核备份保留在 $BACKUP_DIR"
|
||||||
@@ -1014,7 +1014,7 @@ menu() {
|
|||||||
if [[ ${EUID:-$(id -u)} -ne 0 && $warned_root -eq 0 ]]; then
|
if [[ ${EUID:-$(id -u)} -ne 0 && $warned_root -eq 0 ]]; then
|
||||||
warned_root=1
|
warned_root=1
|
||||||
log ""
|
log ""
|
||||||
warn "现在不是 root。查看类操作没问题,但安装/升级/改配置/卸载都会被拒绝,建议 sudo 重跑本脚本"
|
warn "现在不是 root。查看类操作没问题,但安装/升级/修改配置/卸载都会被拒绝,建议 sudo 重新运行本脚本"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local first="🚀 一键安装 ${D}(内核 + systemd + 配置)${N}"
|
local first="🚀 一键安装 ${D}(内核 + systemd + 配置)${N}"
|
||||||
@@ -1030,7 +1030,7 @@ ${W} 选个操作吧:${N}
|
|||||||
${C}5.${N} ⏪ 回滚到上一版内核
|
${C}5.${N} ⏪ 回滚到上一版内核
|
||||||
${C}6.${N} 📦 查看内核备份
|
${C}6.${N} 📦 查看内核备份
|
||||||
${C}7.${N} ⚙️ 配置管理 ${D}(订阅 / 端口 / 面板 / 校验)${N}
|
${C}7.${N} ⚙️ 配置管理 ${D}(订阅 / 端口 / 面板 / 校验)${N}
|
||||||
${C}8.${N} 🎛️ 服务控制 ${D}(启停 / 日志 / 自启)${N}
|
${C}8.${N} 🎛️ 服务控制 ${D}(启动 / 停止 / 日志 / 开机自启)${N}
|
||||||
${C}9.${N} 🗑️ 卸载
|
${C}9.${N} 🗑️ 卸载
|
||||||
${C}0.${N} 👋 退出
|
${C}0.${N} 👋 退出
|
||||||
EOF
|
EOF
|
||||||
@@ -1039,15 +1039,15 @@ EOF
|
|||||||
case "$c" in
|
case "$c" in
|
||||||
1) do_install || warn "安装没走完,看上面的报错"; pause ;;
|
1) do_install || warn "安装没走完,看上面的报错"; pause ;;
|
||||||
2) local cur latest; cur="$(current_version)"; latest="$(latest_version 2>/dev/null || true)"
|
2) local cur latest; cur="$(current_version)"; latest="$(latest_version 2>/dev/null || true)"
|
||||||
if [[ -z "$latest" ]]; then warn "查不到最新版本号,GitHub 大概不通;可以退出后加 --mirror <反代前缀> 重跑"
|
if [[ -z "$latest" ]]; then warn "查不到最新版本号,GitHub 大概不通;可以退出后加 --mirror <反代前缀> 重新运行"
|
||||||
elif [[ -z "$cur" ]]; then warn "内核还没装,选 1 一键安装"
|
elif [[ -z "$cur" ]]; then warn "内核还没安装,选 1 一键安装"
|
||||||
elif [[ "$cur" == "$latest" ]]; then ok "已是最新 $cur"
|
elif [[ "$cur" == "$latest" ]]; then ok "已是最新 $cur"
|
||||||
else warn "有新版: $cur → $latest"
|
else warn "有新版: $cur → $latest"
|
||||||
confirm " 现在升级?" && { do_update "$latest" || warn "升级未完成"; }; fi
|
confirm " 现在升级?" && { do_update "$latest" || warn "升级未完成"; }; fi
|
||||||
pause ;;
|
pause ;;
|
||||||
3) local latest; latest="$(latest_version 2>/dev/null || true)"
|
3) local latest; latest="$(latest_version 2>/dev/null || true)"
|
||||||
if [[ -z "$latest" ]]; then warn "查不到最新版本号,GitHub 大概不通;可以退出后加 --mirror <反代前缀> 重跑"
|
if [[ -z "$latest" ]]; then warn "查不到最新版本号,GitHub 大概不通;可以退出后加 --mirror <反代前缀> 重新运行"
|
||||||
else confirm " 升级/重装内核到 $latest ?" && { FORCE=1; do_update "$latest" || warn "升级未完成"; FORCE=0; }; fi
|
else confirm " 升级/重安装内核到 $latest ?" && { FORCE=1; do_update "$latest" || warn "升级未完成"; FORCE=0; }; fi
|
||||||
pause ;;
|
pause ;;
|
||||||
4) local v; ask " 输入版本号 (如 v1.19.28): " v
|
4) local v; ask " 输入版本号 (如 v1.19.28): " v
|
||||||
if [[ "$v" =~ ^v[0-9] ]]; then FORCE=1; do_update "$v" || warn "安装未完成"; FORCE=0
|
if [[ "$v" =~ ^v[0-9] ]]; then FORCE=1; do_update "$v" || warn "安装未完成"; FORCE=0
|
||||||
@@ -1078,7 +1078,7 @@ ${W} 配置管理${N} ${D}$(conf_file)${N}
|
|||||||
|
|
||||||
${C}1.${N} 🧙 生成配置 ${D}(向导, 会覆盖现有配置)${N}
|
${C}1.${N} 🧙 生成配置 ${D}(向导, 会覆盖现有配置)${N}
|
||||||
${C}2.${N} 🔗 更换订阅链接
|
${C}2.${N} 🔗 更换订阅链接
|
||||||
${C}3.${N} 🔌 改代理端口
|
${C}3.${N} 🔌 修改代理端口
|
||||||
${C}4.${N} 📡 局域网共享 开/关
|
${C}4.${N} 📡 局域网共享 开/关
|
||||||
${C}5.${N} 🎚️ 管理接口 / 面板密钥
|
${C}5.${N} 🎚️ 管理接口 / 面板密钥
|
||||||
${C}6.${N} 📥 从 URL 导入完整配置
|
${C}6.${N} 📥 从 URL 导入完整配置
|
||||||
@@ -1111,7 +1111,7 @@ svc_menu() {
|
|||||||
${C}1.${N} ▶️ 启动 ${C}2.${N} ⏹️ 停止 ${C}3.${N} 🔄 重启
|
${C}1.${N} ▶️ 启动 ${C}2.${N} ⏹️ 停止 ${C}3.${N} 🔄 重启
|
||||||
${C}4.${N} 📊 状态 ${C}5.${N} 📜 最近日志
|
${C}4.${N} 📊 状态 ${C}5.${N} 📜 最近日志
|
||||||
${C}6.${N} ⏻ 开机自启 开/关 ${C}7.${N} 🧾 重写服务文件
|
${C}6.${N} ⏻ 开机自启 开/关 ${C}7.${N} 🧾 重写服务文件
|
||||||
${C}8.${N} 🔗 装/刷新 $(basename "$LAUNCHER") 快捷命令
|
${C}8.${N} 🔗 安装 / 刷新 $(basename "$LAUNCHER") 快捷命令
|
||||||
${C}0.${N} ↩️ 返回
|
${C}0.${N} ↩️ 返回
|
||||||
EOF
|
EOF
|
||||||
local c; ask "$(printf '\n %s>%s ' "$W" "$N")" c
|
local c; ask "$(printf '\n %s>%s ' "$W" "$N")" c
|
||||||
@@ -1124,7 +1124,7 @@ EOF
|
|||||||
6) if [[ "$(svc_enabled)" == "enabled" ]]; then
|
6) if [[ "$(svc_enabled)" == "enabled" ]]; then
|
||||||
systemctl disable "$SERVICE" && ok "已关闭开机自启"
|
systemctl disable "$SERVICE" && ok "已关闭开机自启"
|
||||||
else
|
else
|
||||||
systemctl enable "$SERVICE" && ok "已打开开机自启"
|
systemctl enable "$SERVICE" && ok "已设为开机自启"
|
||||||
fi ;;
|
fi ;;
|
||||||
7) install_unit && ok "服务文件已刷新;生效需要重启服务" || true ;;
|
7) install_unit && ok "服务文件已刷新;生效需要重启服务" || true ;;
|
||||||
8) install_launcher || true ;;
|
8) install_launcher || true ;;
|
||||||
@@ -1136,7 +1136,7 @@ EOF
|
|||||||
# ───────────────────────── 参数解析 ─────────────────────────
|
# ───────────────────────── 参数解析 ─────────────────────────
|
||||||
CMD=""
|
CMD=""
|
||||||
if [[ $# -eq 0 ]]; then
|
if [[ $# -eq 0 ]]; then
|
||||||
# stdin 是终端就进菜单;被重定向/管道(cron、CI)就安静地跑 check
|
# stdin 是终端就进菜单;被重定向/管道(cron、CI)就安静地运行 check
|
||||||
if [[ -t 0 ]] || { [[ -t 1 ]] && [[ -r /dev/tty ]]; }; then CMD="menu"; else CMD="check"; fi
|
if [[ -t 0 ]] || { [[ -t 1 ]] && [[ -r /dev/tty ]]; }; then CMD="menu"; else CMD="check"; fi
|
||||||
else
|
else
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -1191,7 +1191,7 @@ case "$CMD" in
|
|||||||
(( JSON )) && printf '{"current":"%s","latest":"%s","outdated":%s}\n' \
|
(( JSON )) && printf '{"current":"%s","latest":"%s","outdated":%s}\n' \
|
||||||
"${CUR:-null}" "$TARGET" "$([[ "$CUR" != "$TARGET" ]] && echo true || echo false)"
|
"${CUR:-null}" "$TARGET" "$([[ "$CUR" != "$TARGET" ]] && echo true || echo false)"
|
||||||
log "当前: ${CUR:-${Y}未安装${N}} 最新: $TARGET"
|
log "当前: ${CUR:-${Y}未安装${N}} 最新: $TARGET"
|
||||||
[[ -z "$CUR" ]] && { log "→ 还没装,跑 install 一键安装"; exit 10; }
|
[[ -z "$CUR" ]] && { log "→ 还没安装,运行 install 一键安装"; exit 10; }
|
||||||
[[ "$CUR" == "$TARGET" ]] && { ok "已是最新"; exit 0; }
|
[[ "$CUR" == "$TARGET" ]] && { ok "已是最新"; exit 0; }
|
||||||
warn "有新版本可升级"; exit 10 ;;
|
warn "有新版本可升级"; exit 10 ;;
|
||||||
update)
|
update)
|
||||||
|
|||||||
Reference in New Issue
Block a user