一、需求背景

在一台运行 Ubuntu Server 的树莓派上,已经存在稳定可用的有线网络连接。与此同时,还希望接入无线网络,以满足以下需求:

  • 有线网络作为长期稳定的保底入口,确保始终可以远程 SSH 管理
  • 无线网络在需要时手动启用,以获得更高的带宽或更灵活的接入方式
  • 当有线和无线同时在线时,希望无线优先、有线备用
  • 不希望将无线网络永久写入系统主网络配置,避免在外部环境中产生不必要的自动连接与报错
  • 希望通过命令行完成 Wi-Fi 扫描、选择和连接,适合后续用手机 SSH 工具远程操作

这一需求本质上不是“桌面式自动联网”,而是“服务器式可控联网”。


二、系统现状判断

首先需要确认系统中可用的网络工具,以及当前的网络管理方式。检查结果显示:

  • 系统存在 netplan
  • 系统存在 wpa_supplicant
  • 系统存在 ip
  • 系统使用的是 systemd-networkd
  • 不存在 nmclinmtui
  • 无线接口已被系统识别,但默认未启用

这说明该环境属于典型的 Ubuntu Server 风格:

  • 没有桌面端常见的交互式 Wi-Fi 管理工具
  • 无法像桌面系统一样直接弹出菜单选择无线网络
  • 更适合通过底层命令和脚本完成无线连接管理

因此,后续方案不采用 NetworkManager,不依赖图形界面,而是基于以下工具链完成:

  • iw / iwlist:扫描无线网络
  • wpa_supplicant:建立 Wi-Fi 连接
  • dhclient:获取 DHCP 地址
  • ip route:控制路由优先级

三、总体策略

本次实践最终形成了如下结构:

1. 有线网络长期保留

有线接口始终保持可用,作为系统的远程管理保底入口。

这样做的意义很大:

  • 即使无线连接失败,也不会失去 SSH 控制权
  • 即使在外部环境中搜不到目标 Wi-Fi,系统仍然可管理
  • 不会因为无线配置问题把自己“锁”在系统外

2. 无线网络按需启用

无线不作为系统的开机自动连接项,而是在需要时手动触发:

  • 在家庭环境中,可手动连接家庭 Wi-Fi
  • 在咖啡馆、临时网络环境中,也可手动连接公共 Wi-Fi
  • 不修改系统的长期网络主配置,不污染系统默认行为

3. 当双接口同时在线时,设为 Wi-Fi 优先

这是通过调整默认路由 metric 实现的:

  • 无线默认路由 metric 更低
  • 有线默认路由 metric 更高
  • 从而使系统实际出站优先走无线
  • 有线继续保留,作为备份链路

最终实现的逻辑是:

有线常驻保底,Wi-Fi 按需启用;启用后 Wi-Fi 优先,有线备用。


四、工具安装与基础验证

为了支持命令行下的无线操作,需要安装以下工具:

  • iw
  • wireless-tools
  • wpasupplicant
  • isc-dhcp-client

安装完成后,需要验证几个关键点:

  1. 无线扫描工具是否存在
  2. 连接工具是否存在
  3. DHCP 客户端是否存在
  4. 无线接口是否已被内核识别

实际验证结果表明:

  • 所需工具全部可用
  • 无线接口已存在
  • 只是初始状态为未启用

这说明系统层面已经具备无线接入能力。


五、扫描附近 Wi-Fi

在命令行环境下,第一步是先拉起无线接口,然后扫描附近无线网络。

扫描结果中能够看到多个 SSID,说明:

  • 无线硬件工作正常
  • 驱动正常
  • 扫描链路正常
  • 后续已经具备连接条件

值得注意的是,扫描结果中个别中文 SSID 可能以转义形式显示。这并不意味着扫描失败,只是终端编码显示方式不同。


六、为什么不直接修改系统主配置

在服务器环境中,最常见的做法之一,是直接将 Wi-Fi 写进 netplan 或其它系统主网络配置中,实现开机自动连接。

但在本场景下,这种做法并不是最优解。原因有三点。

1. 远程操作场景更需要可控性

后续可能经常通过手机 SSH 工具远程进入系统。如果每次修改都依赖主配置文件,会增加操作复杂度,也提高误配置风险。

2. 外部环境不固定

无线网络并不总是同一个:

  • 家庭网络
  • 咖啡馆公共网络
  • 临时热点

如果长期绑定某个无线配置,系统在找不到目标网络时会出现额外的失败尝试或日志噪音。

3. 有线已经足够承担“始终在线”的职责

既然已有有线作为保底入口,就没有必要把无线也做成强绑定的自动基础设施。无线更适合作为可插拔能力,而不是系统底盘的一部分。

因此,最终放弃“永久写入系统主配置”的思路,转而采用脚本化、按需启用的方案。


七、从手动命令到脚本化管理

最初的无线接入过程包括以下步骤:

  1. 手动启用无线接口
  2. 扫描 SSID
  3. 手动输入目标 SSID
  4. 输入密码
  5. 生成临时连接配置
  6. 启动 wpa_supplicant
  7. 通过 DHCP 获取地址
  8. 调整默认路由优先级

这套流程本身没有问题,但重复执行会比较繁琐。因此,后续将其整理为脚本。


八、wifi-connect 脚本的设计思路

为了满足日常使用需求,连接脚本被设计为具备以下特性。

1. 自动扫描附近 Wi-Fi

脚本启动后先扫描并列出附近所有可见 SSID,避免每次手动敲扫描命令。

2. 支持数字编号选择

为提升交互体验,脚本不再要求手动输入完整 SSID,而是将扫描结果编号后列出,用户只需输入数字即可选择目标网络。

这比直接输入无线名称更适合在手机 SSH 环境中操作。

3. 密码隐藏输入

密码通过隐藏模式读取,不会直接显示在终端中,避免明文暴露在 shell 输入记录中。

4. 使用临时配置文件

Wi-Fi 连接配置生成在临时位置,脚本退出后删除,从而避免长期在系统中残留明文连接信息。

5. 连接成功后自动设置优先级

脚本会在获取无线地址后,将无线默认路由调整为更低的 metric,从而让无线成为主线路。

6. 保留有线网络不动

脚本不会破坏现有有线连接,因此整个过程始终保有一个稳定入口。


九、wifi-disconnect 的作用

除了连接脚本,还增加了一个断开脚本。

它的作用很简单:

  • 停掉无线连接进程
  • 释放无线 DHCP 地址
  • 关闭无线接口

这样做之后,系统就会自然回到仅有线运行的状态。

这个脚本虽然不复杂,但很实用。因为它把“回到纯有线状态”这件事也做成了标准化操作。


十、路由优先级确认过程

在双接口都连通之后,最关键的问题不是“两个网都在线”,而是:

系统实际出站到底走哪一个?

检查方式包括:

  • 查看 ip route
  • 查看 ip route get 外部地址

通过这些结果可以明确确认:

  • 默认路由存在两条时,metric 更小的那条优先
  • 无线被设为较低 metric 后,系统实际对外访问走无线
  • 有线继续保留默认路由,但作为备用

在调试过程中,曾出现过无线默认路由重复的问题,即同一接口存在两条默认路由,其中一条没有 metric,另一条显式指定了 metric。这种状态虽然不一定立即导致故障,但不够干净。

后续通过修正脚本中的路由处理逻辑,最终将路由表整理为清晰状态:

  • 无线默认路由:metric 较低
  • 有线默认路由:metric 较高

这样网络行为就完全明确了。


十一、为什么最终放弃“家庭 Wi-Fi 自动连接”

在后续讨论中,曾考虑将家庭 Wi-Fi 设为自动连接,而外部公共网络继续使用脚本临时连接。

这个思路看似合理,但最终还是放弃了自动连接,原因很明确:

1. 自动连接并非必要

因为系统已经有有线网络作为常驻保底入口,所以即使不自动连接 Wi-Fi,也不会影响远程管理能力。

2. 自动连接会引入环境判断问题

在外部环境中,如果系统持续尝试连接家里的 Wi-Fi,就可能带来不必要的失败日志或无意义扫描。

3. 手动连接更符合服务器管理思路

本机本质上不是一个“拿起来随时漫游”的消费型终端,而是一台可以远程管理的服务器。对于这类设备:

  • 稳定入口比自动化更重要
  • 可控性比便利性更重要
  • 明确操作比隐式行为更可靠

因此最终确定:

  • 不启用家庭 Wi-Fi 自动连接
  • 保留纯手动脚本式无线接入
  • 有线网络作为长期底盘

这让整套方案更加简洁。


十二、脚本实现

下面给出本次实践最终形成的两个脚本版本。内容做了适度脱敏,但保留了完整逻辑。

1. 连接脚本:wifi-connect

这个脚本完成的工作包括:

  • 启用无线接口
  • 扫描附近 Wi-Fi
  • 去重并编号显示 SSID
  • 通过数字选择目标网络
  • 隐藏输入密码
  • 生成临时 wpa_supplicant 配置
  • 启动 Wi-Fi 连接
  • 获取 DHCP 地址
  • 设置无线优先于有线
  • 输出最终结果
#!/bin/bash
set -euo pipefailIFACE="wlan0"
CONF="/tmp/${IFACE}-wpa.conf"cleanup() {
rm -f "$CONF"
}
trap cleanup EXITecho "== Bringing up ${IFACE} =="
ip link set "$IFACE" upecho
echo "== Scanning nearby Wi-Fi =="mapfile -t SSIDS < <(
iw dev "$IFACE" scan 2>/dev/null \
| sed -n 's/^[[:space:]]*SSID: //p' \
| awk 'NF && !seen[$0]++'
)if [ "${#SSIDS[@]}" -eq 0 ]; then
echo "No Wi-Fi SSIDs found."
exit 1
fiecho
echo "== Nearby Wi-Fi =="
for i in "${!SSIDS[@]}"; do
printf "%2d) %s\n" "$((i+1))" "${SSIDS[$i]}"
doneecho
read -r -p "Select Wi-Fi number: " NUMif ! [[ "$NUM" =~ ^[0-9]+$ ]]; then
echo "Invalid number."
exit 1
fiif [ "$NUM" -lt 1 ] || [ "$NUM" -gt "${#SSIDS[@]}" ]; then
echo "Number out of range."
exit 1
fiSSID="${SSIDS[$((NUM-1))]}"echo "Selected: $SSID"
read -r -s -p "Enter password: " PASS
echoumask 077
wpa_passphrase "$SSID" "$PASS" > "$CONF"
unset PASSpkill wpa_supplicant 2>/dev/null || true
dhclient -r "$IFACE" 2>/dev/null || trueecho
echo "== Connecting to ${SSID} =="
wpa_supplicant -B -i "$IFACE" -c "$CONF"
dhclient "$IFACE"GW="$(ip route | awk '$1=="default" && $5=="wlan0" {print $3; exit}')"
if [ -n "${GW:-}" ]; then
ip route del default via "$GW" dev "$IFACE" 2>/dev/null || true
ip route add default via "$GW" dev "$IFACE" metric 50
fiecho
echo "== Result =="
ip addr show "$IFACE"
echo
ip route

2. 断开脚本:wifi-disconnect

这个脚本用于将系统从“有线+无线双接入”状态恢复为“仅有线”状态。

#!/bin/bash
set -euo pipefailIFACE="wlan0"echo "== Disconnecting ${IFACE} =="pkill wpa_supplicant 2>/dev/null || true
dhclient -r "$IFACE" 2>/dev/null || true
ip link set "$IFACE" down || trueecho
ip addr show "$IFACE" || true
echo
ip route

十三、脚本设计上的几个关键点

1. 配置文件写入临时目录

连接脚本并没有将无线配置写入长期位置,而是放在临时目录中,并在脚本退出时清理。这意味着:

  • 不会在系统里长期残留连接信息
  • 不会形成“默认自动连接”
  • 更适合临时网络场景

2. 密码不直接出现在命令行参数中

脚本采用隐藏输入,而不是把密码直接写在 shell 命令中。这样可以减少以下问题:

  • 终端屏幕直接暴露明文
  • 复制粘贴时泄露
  • shell 历史中留下完整命令

虽然从绝对安全角度讲,任何连接过程都不可能做到“完全无痕”,但在日常远程管理场景中,这已经是一个比较平衡的做法。

3. 路由 metric 明确控制优先级

连接脚本在拿到无线默认路由后,会主动删除旧的无线默认路由,再添加带 metric 的新路由,避免出现重复默认路由的问题。

最终效果是:

  • 无线默认路由 metric 较小
  • 有线默认路由 metric 较大
  • 实际对外访问优先走无线

4. 有线网络完全不参与脚本销毁流程

脚本只操作无线接口,不触碰有线接口。这是整套方案最重要的稳定性保证之一。


十四、实际验证结果

在脚本调通之后,系统最终呈现出以下状态:

  • 有线接口在线
  • 无线接口在线
  • 无线获取到独立地址
  • 默认路由中同时存在有线和无线
  • 无线 metric 更低
  • ip route get 验证实际出站走无线

也就是说,设计目标已经完整实现:

  • 有线是保底链路
  • 无线是主用链路
  • 两者同时在线时行为明确
  • 断开无线后系统自然回落到纯有线状态

十五、为什么这套方案适合服务器而不是桌面机

很多桌面环境会倾向于:

  • 自动记忆 Wi-Fi
  • 自动重连
  • 自动切换最优网络

但在服务器场景中,尤其是远程管理场景中,这种“方便优先”的策略未必最优。

本方案更接近服务器思维:

  • 永远保留一个稳定入口
  • 额外能力通过显式操作启用
  • 不让系统产生过多隐式行为
  • 不让无线配置污染长期主配置
  • 在出现异常时,仍然可以通过有线入口救援

对于小型 ARM 服务器、树莓派边缘节点、家用实验机、远程开发盒子等场景,这种方式往往更可靠。


十六、最终方案总结

本次实践最终形成的结构可以概括为:

1. 有线网络长期在线

作为远程 SSH 管理入口和系统底层保障。

2. 无线网络按需手动启用

通过脚本扫描、选择、隐藏输入密码、连接。

3. 双接口同时在线时,无线优先

通过 metric 控制路由优先级,实现无线主用、有线备用。

4. 不改系统主网络配置

避免引入自动连接、副作用和环境依赖问题。

5. 外部网络环境仍然适配

在家庭网络、公共网络、临时热点之间切换时,都可以继续复用同一套脚本逻辑。


十七、这套方案的价值

这次实践的真正价值,不只是“连上了 Wi-Fi”,而是建立了一种适合服务器场景的网络管理思路:

  • 不追求桌面式傻瓜自动化
  • 不把所有东西都永久写死在配置文件里
  • 有稳定保底入口
  • 需要时再启用附加能力
  • 对网络路径和系统行为保持明确控制

对于长期运行的树莓派或小型服务器来说,这种方式往往比“全自动、全写死”的方案更稳妥。

它不是最花哨的方案,但非常适合强调可控性、可维护性和远程管理可靠性的场景。

Leave a Reply

Your email address will not be published. Required fields are marked *