1. 双网卡切换,为什么值得写个脚本
1.1 最常见的几类双网卡场景
我做了多年桌面运维和网络支持,遇到“双网卡”的频率比想象中高得多。最常见的是三类场景。
第一类是内网和外网隔离。办公网里存着内部系统、文件服务器、打印机,另一张网卡接的是宽带或手机开的热点,用于互联网访问。有些团队为了安全要求,明确规定这两条链路不能同时在线,员工每天要在两种模式之间来回切。第二类是研发和测试环境。机器一台连着办公网络,另一张无线网卡要连到设备调试网段,或者连到一台临时搭的路由器上,每次切换都涉及 IP、网关、DNS 全套参数。第三类是有线和无线互为备份。无线信号不稳定时切到有线,有线被拔掉时切回无线,虽然不频繁,但一旦要切,通常都是赶时间的时候。
不管是哪类场景,本质问题都一样:一台电脑上有两张可用网卡,但同一时刻只能以其中一种网络配置作为主链路。命令行脚本存在的意义,就是把“禁用一块网卡、启用另一块、重置路由、刷新 DNS、验证连通性”这一连串操作合并成一次执行。
1.2 手动切网到底慢在哪
在图形界面里手动切换一次,步骤大致是这样的:右键网络图标、进入网络适配器选项、找到目标网卡、右键禁用或启用、再进入 IPv4 属性、改 IP 和网关、有时还要改 DNS、最后打开命令行执行ipconfig /flushdns。整套流程顺利的话也要一分钟,稍微一犹豫,或者系统网络服务卡一下,三五分钟都很正常。
比慢更麻烦的是容易出错。公司内部网络如果用的静态 IP,你得记住那串数字;如果 DHCP 获取,还要等它超时。最恼火的是两张网卡都处于启用状态时,系统会同时看到两个默认网关,它可能继续走旧的网卡,结果就是你感觉自己切过去了,实际上访问内网资源还是不通。这种状态特别难排查,因为你刚改完 IP,桌面右下角的网络图标看起来是正常的,但路由表里早就乱套了。
1.3 脚本化能解决什么
把切换动作写进脚本后,解决的问题不只是快。更关键的是确定性。每次执行都是同一套参数、同一个顺序,不会因为手抖改错一位 IP;切换完还能自动验证,通了就在屏幕上显示结果,没通则提示失败。再有就是可以交给别人执行。很多同事对网络设置并不熟,他们只需要双击一个切换入口,然后告诉我切完了,剩下的事由脚本保证。
所以我不建议用临时的“手输命令”来应对高频切换。整理成脚本,本质上是把一次性的操作变成可重复、可维护、可交接的资产。
2. 动手前先搞懂路由表和网络优先级
2.1 电脑是怎么决定数据走哪张网卡的
要理解双网卡切换,必须先看路由表。很多人以为切换网卡就是“启用哪块用哪块”,实际上操作系统是查路由表来决定数据包交给哪块网卡的。
在命令行里输入route print -4,你会看到一张 IPv4 路由表,核心是这么几列:
网络目标 网络掩码 网关 接口 跃点数 0.0.0.0 0.0.0.0 192.168.1.1 192.168.1.100 35 0.0.0.0 0.0.0.0 10.10.20.1 10.10.20.5 30 10.10.20.0 255.255.255.0 On-link 10.10.20.5 276 192.168.1.0 255.255.255.0 On-link 192.168.1.100 291其中0.0.0.0那两条是默认路由,代表“所有不知道往哪走的数据包都交给它”。如果两条默认路由同时存在,系统会优先选择“跃点数”更小的那条。比如上面表格里走内网网关的默认路由跃点数是 30,走外网网关的是 35,那么所有上网流量会优先走内网。听起来可能没问题,但假如你现在想访问互联网,流量却被送往内网网关,内网网关没有互联网出口,数据就丢了。
所以双网卡切换脚本里的一项重要工作,就是管理默认路由。要么把不需要的默认路由删掉,要么把目标网卡的默认路由跃点数调成最小,确保切完之后,默认流量只会走当前想要走的那张网卡。
2.2 网卡状态、IP 配置、路由跃点有什么关系
三者其实是联动的。禁用一块网卡后,它的所有路由条目都会从路由表里消失;启用网卡后,系统会根据网卡上配置的 IP、子网掩码、网关自动生成直连路由和默认路由。也就是说,只改路由理论上是可行的,但网卡状态不改变,系统可能会在某个时刻因为 DHCP 续租或网络状态变化重新生成一条你不想看到的路由。
手动切换最容易漏掉的就是这一点。你在网络连接面板里把有线网卡禁用了,但无线网卡迟迟没拿到地址,或者默认路由还残留着有线网卡的信息,网络状态就会很混乱。脚本的做法通常是一套组合拳:先禁用当前不需要的网卡,再启用目标网卡,然后清理默认路由,最后手动添加一条稳定的默认路由,并指定它的跃点数。
顺带说一下移动网络的自动跃点机制。Windows 默认会给接口开启自动跃点,系统根据链路速度等参数自动计算。这很方便,但在双网卡场景下,自动计算的结果不一定符合你的期望,所以脚本里最好手动指定跃点数。跃点数只表示优先级,数字不一定是越小越“快”,它只是让系统知道哪条路由优先使用。
2.3 为什么切换后经常出现“网络假通”
最常见的“假通”现象是:右下角网络图标显示已连接,但访问公司内网服务器超时。原因多半是默认路由还指在不该指的网卡上,或者 DNS 用的还是另一张网卡下发的地址。
DNS 这个问题很隐蔽。即使你把默认路由切过去了,如果网卡上配置的 DNS 还是旧网络下的地址,解析域名时还是会失败。所以我的脚本里一般都会顺手把 DNS 也设好,或者至少执行一次ipconfig /flushdns清理缓存。切换后验证不要只 ping 网关,还要同时验证 DNS 解析和实际访问,才能真正确认“切换成功”。
3. 方案选型:批处理还是 PowerShell
3.1 两种脚本怎么选
写 Windows 下的命令行脚本,主要就两个选择:传统批处理(.bat/.cmd)和 PowerShell(.ps1)。我做过一个对比,方便你按自己的环境来选:
| 对比项 | 批处理 | PowerShell |
|---|---|---|
| 系统兼容性 | Windows 7 到 11 都很好 | Windows 7 SP1 之后可用,但越新的系统越友好 |
| 语法复杂度 | 简单直接,容易上手 | 初看比较复杂,但逻辑更清晰 |
| 错误处理 | 弱,出错继续往下走 | 强,可以 catch 异常 |
| 网卡操作 | 靠netsh间接操作 | 有专门的NetAdapter模块 |
| 适合人群 | 老运维、快速写小工具 | 有条件写更复杂逻辑的人 |
如果你只是需要“双击运行、马上切换”,批处理完全够用。但如果你想做更复杂的判断,比如“先 ping 网关,通了才切换”或者“切换失败自动回滚”,PowerShell 写起来更顺手。我的建议是,老机器、老系统上部署,优先批处理;自己维护的机器或者公司标准环境是 Windows 10 以上,直接上 PowerShell,后续扩展空间更大。
3.2 先把环境信息摸清楚
写脚本之前,必须先确认三件事:网卡叫什么名字、当前 IP 网段是什么、路由表和接口索引长什么样。
看网卡名称最直接的方式是Get-NetAdapter:
Get-NetAdapter | Format-Table Name, InterfaceDescription, Status, LinkSpeed中文系统里,有线网卡通常叫“以太网”,无线网卡通常叫“WLAN”;英文系统一般叫Ethernet和Wi-Fi。这个名字不固定,取决于网卡驱动和系统设置。建议在脚本里写成变量,方便以后改。
查看路由表和接口索引用route print -4,开头部分会列出所有接口列表,里面会有每个接口的 IDX 编号,比如:
接口列表 12...a1 b2 c3 d4 e5 f6 ......Intel(R) Ethernet Connection 8...11 22 33 44 55 66 ......Intel(R) Dual Band Wireless-AC 8265这个 IDX 编号在后面指定路由时必须用到。
3.3 权限和脚本执行策略,提前准备好
修改网卡状态和路由表,必须要有管理员权限。直接双击批处理,通常会用普通权限跑,所以脚本内部最好第一件事就检查权限,没有权限就直接退出,别让它执行到一半报一堆不明不白的错。
PowerShell 还有一层执行策略限制。默认情况下.ps1文件可能会被系统拦截,运行报错“因为在此系统上禁止运行脚本”。解决办法是给当前用户放开限制,但没必要改成不受限制,用这个命令就够了:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser设置完以后,本地创建的脚本可以运行,从网上下载的脚本如果没签名则会被拦截,兼顾安全和便利。这些前置工作都说清楚以后,就可以进入正题写脚本了。
4. 完整脚本实现与关键参数说明
4.1 批处理版:适合快速部署的一键切换脚本
先给出一个可以直接用的批处理版本。这个脚本实现“内网模式”和“外网模式”的切换,假设有线网卡叫“以太网”,无线网卡叫“WLAN”,内网网关是 10.10.20.1,外网网关是 192.168.1.1。保存时注意,中文系统下批处理建议用 GBK 编码保存,避免注释和提示文字乱码。
@echo off chcp 65001 >nul net session >nul 2>&1 if %errorlevel% neq 0 ( echo 请以管理员身份运行本脚本。 pause exit /b 1 ) set MODE=%1 if "%MODE%"=="" ( echo 用法: switch-net.bat [intranet^|internet] pause exit /b 1 ) if /i "%MODE%"=="intranet" ( echo 正在切换到内网模式... netsh interface set interface "WLAN" disabled netsh interface set interface "以太网" enabled route delete 0.0.0.0 mask 0.0.0.0 route add 0.0.0.0 mask 0.0.0.0 10.10.20.1 metric 20 ipconfig /flushdns >nul echo 内网模式切换完成。 ) else if /i "%MODE%"=="internet" ( echo 正在切换到外网模式... netsh interface set interface "以太网" disabled netsh interface set interface "WLAN" enabled route delete 0.0.0.0 mask 0.0.0.0 route add 0.0.0.0 mask 0.0.0.0 192.168.1.1 metric 20 ipconfig /flushdns >nul echo 外网模式切换完成。 ) else ( echo 未知模式: %MODE% echo 用法: switch-net.bat [intranet^|internet] ) pause这里有两处关键点。第一处是route delete 0.0.0.0 mask 0.0.0.0,它会删掉当前所有默认路由。注意,如果你有多张网卡同时在线,删除默认路由后所有网卡的默认路由都会被删掉,然后再统一添加一条我们想要的。
第二处是route add 0.0.0.0 mask 0.0.0.0 10.10.20.1 metric 20,这里的metric 20会让这条路由有比较高的优先级。如果你还需要同时保留另一张网卡的直连网段访问,可以追加静态路由,但大多数人只是切主链路,所以默认路由覆盖掉就够了。
4.2 PowerShell 版:逻辑更清晰,适合扩展
PowerShell 版本对初学者稍难一点,但可读性和可维护性明显更好。脚本同样实现两种模式,并把网卡名和路由参数抽成变量,以后改起来不用翻到命令中间去找。
param( [Parameter(Mandatory=$true)] [ValidateSet("intranet", "internet")] [string]$Mode ) $ErrorActionPreference = "Stop" $admin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) if (-not $admin) { Write-Host "请以管理员身份运行" -ForegroundColor Red exit 1 } $Ethernet = "以太网" $Wlan = "WLAN" switch ($Mode) { "intranet" { Disable-NetAdapter -Name $Wlan -Confirm:$false Enable-NetAdapter -Name $Ethernet -Confirm:$false Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Remove-NetRoute -Confirm:$false New-NetRoute -DestinationPrefix "0.0.0.0/0" -InterfaceAlias $Ethernet -NextHop "10.10.20.1" -RouteMetric 20 Set-DnsClientServerAddress -InterfaceAlias $Ethernet -ServerAddresses "10.10.20.1" Clear-DnsClientCache Write-Host "已切换到内网模式" -ForegroundColor Green } "internet" { Disable-NetAdapter -Name $Ethernet -Confirm:$false Enable-NetAdapter -Name $Wlan -Confirm:$false Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Remove-NetRoute -Confirm:$false New-NetRoute -DestinationPrefix "0.0.0.0/0" -InterfaceAlias $Wlan -NextHop "192.168.1.1" -RouteMetric 20 Set-DnsClientServerAddress -InterfaceAlias $Wlan -ServerAddresses "223.5.5.5" Clear-DnsClientCache Write-Host "已切换到外网模式" -ForegroundColor Green } }PowerShell 的优势集中在三处:ValidateSet限制了参数只能填intranet或internet,少敲错一个字母;Disable-NetAdapter和Enable-NetAdapter这类命令比netsh interface set更直观;$ErrorActionPreference = "Stop"让脚本在出错时停下,而不是蒙着头继续跑。
有一点需要特别说明:如果脚本要放在共享位置或者作为计划任务运行,注意网卡名的空格和中文,-InterfaceAlias直接用字符串就行,不用转义。另外,New-NetRoute的-NextHop是网关地址,-RouteMetric是跃点数,这两个参数和批处理版里的route add是对应的。
4.3 route add 里的 if 和 metric 到底怎么填
很多人第一次看route add的帮助文档会被if参数弄晕。它的作用是指定路由绑定到哪个接口。在某些情况下,不写if也可以,但那是系统有明确的“最优接口”时才可靠;在双网卡竞争时,一定要显式指定,否则即使网关是对的,系统也可能把数据包交给错误的网卡。
怎么看接口索引?执行route print -4,最上方就有接口列表。假设有线网卡接口索引是 12,那么添加内网默认路由的命令可以写成:
route add 0.0.0.0 mask 0.0.0.0 10.10.20.1 if 12 metric 20metric在 Windows 中默认是自动计算的,但手动指定能确保优先级。逻辑上,你想要用的网卡,跃点数设小一点;不常用的网卡,跃点数设大一点。这样即使两块网卡都开着,系统也会优先走你要的那条默认路由。
如果觉得接口索引在重启后可能变化,可以在批处理里先用netsh interface ip show config查看当前名称,再配合route print人工核对。更一劳永逸的做法是直接给网卡命名固定,比如把有线网卡改名为LAN,无线网卡改名为WIFI,脚本里就用这两个名称。
4.4 加日志、加提示、加隐藏运行
脚本跑起来没有反馈,是最容易被忽略的问题。我习惯在批处理里输出一条时间戳日志:
echo [%date% %time%] 切换到内网模式 >> D:\scripts\net-switch.logPowerShell 版本可以用Add-Content:
Add-Content -Path "D:\scripts\net-switch.log" -Value "[$(Get-Date)] 切换到 $Mode 模式"日志不用写得很复杂,记录“什么时候、切到了什么模式”就够了。万一以后有人问你“网络是谁弄坏的”,翻一下日志立刻能定位。
再有一个实用技巧是隐藏运行窗口。如果脚本是通过计划任务触发,或者你想做成一个完全无感的双击工具,不希望命令行窗口一直弹出来,可以用一个 VBS 脚本包裹执行,窗口模式设为 0:
Set ws = CreateObject("Wscript.Shell") ws.Run "powershell -NoProfile -ExecutionPolicy Bypass -File D:\scripts\switch-net.ps1 intranet", 0, True把这段存成switch-intranet.vbs,双击时 PowerShell 窗口不会闪出来,但会静默执行。注意这里用了0表示隐藏窗口,True表示等待执行完成后再退出。缺点是一旦出错,你也不知道发生了什么,所以隐藏运行的方案最好搭配日志一起用。
5. 实战操作流程:从部署到验证
5.1 一个典型测试环境
为了把整个流程讲清楚,我以手头一台测试笔记本为例,它的环境长这样:
| 网卡 | 网络类型 | IP 配置 | 网关 | DNS |
|---|---|---|---|---|
| 以太网 | 内网办公 | 静态 IP 10.10.20.5/24 | 10.10.20.1 | 10.10.20.1 |
| WLAN | 互联网 | DHCP 自动获取 | 192.168.1.1 | 223.5.5.5 |
目标模式也很清晰:切到“内网模式”时,停用无线网卡,启用有线网卡,默认路由指到内网网关;切到“外网模式”时,停用有线网卡,启用无线网卡,默认路由指到路由器网关。这样设计模式的好处是不会冲突,因为同时只有一张网卡在线。
5.2 部署脚本的具体步骤
第一步,在D:\scripts目录下创建switch-net.ps1,把上面的 PowerShell 脚本保存进去。
第二步,给当前用户开放执行策略:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser第三步,建一个桌面快捷方式,目标填:
powershell -NoProfile -ExecutionPolicy Bypass -File D:\scripts\switch-net.ps1 intranet第四步,快捷方式属性里勾选“以管理员身份运行”。这一步很容易漏,如果你不想每次弹 UAC,可以在计划任务里创建一个“交互式登录时运行”的任务,用最高权限运行,再在快捷方式里直接指向这个计划任务。这样体验最顺滑,但配置成本稍微高一点。
给同事使用时,我会再顺手做一个“外网模式”的快捷方式,对应internet参数,两个入口清清楚楚。
5.3 切换后的验证,别只 ping 一下
脚本跑完不代表一定成功,验证要分层做。我一般按顺序执行这几条命令:
ipconfig /all route print -4 ping 10.10.20.1 ping 223.5.5.5 nslookup www.example.com第一次验证是看网卡状态,确认目标网卡已经启用,IP 和网关对不对。第二次是看默认路由,确保0.0.0.0指向我们想要的网关,并且只有一条默认路由。第三次验证网关连通性,第四次验证互联网连通性。最后再用nslookup验证 DNS 解析。如果ping网关通了但nslookup失败,问题多半出在 DNS 上,回到脚本里检查Set-DnsClientServerAddress这段。
如果你在公司内部,内网访问往往不只是通过默认路由,还可能需要访问多个内网网段。这时候就要在脚本里追加静态路由,比如:
route add 172.16.0.0 mask 255.240.0.0 10.10.20.1 metric 20追加之后,不仅内网网关所在网段可以通,整个 172.16.0.0/12 网段也能通过内网网关访问,不影响默认路由指向互联网。
5.4 切换失败时怎么快速回退
脚本执行到一半报错,最怕的是网卡被禁用后无法恢复。我的经验是准备一个“恢复出厂”小脚本,里面只有两行命令:
netsh interface set interface "以太网" enabled netsh interface set interface "WLAN" enabled不管当前处于什么状态,先把两块网卡都启用,然后重新进入图形界面检查 IP 和网关,至少能保证设备从一个可操作的状态重新开始。如果是因为手动配置了错误的静态 IP 导致无法上网,可以直接把网卡改成 DHCP 获取,再让它重新拿地址。
另外提醒一句,如果你通过远程桌面或 SSH 连接到那台机器,千万别贸然禁用当前正在使用的网卡,否则远程连接会立刻断开。脚本里加一个确认提示也行,或者明确告诉使用者“切换后远程连接会断一下,需要用另一张网卡的地址重新连接”。
6. 常见问题与排查技巧实录
6.1 问题速查表
把实践中高频出现的问题整理成一张速查表,比抱着文档翻舒服得多。
| 问题现象 | 常见原因 | 排查与解决办法 |
|---|---|---|
| 提示需要管理员权限 | 脚本没有用管理员身份运行 | 右键脚本,选择“以管理员身份运行”,或检查计划任务最高权限 |
| 网卡名称识别不了 | 系统中网卡名不是“以太网”/“WLAN” | 执行Get-NetAdapter确认实际名称,修改脚本变量 |
| route add 报参数错误 | 网关不可达或缺少 if 参数 | 先确认网关地址,再用route print -4找到接口索引并显式填写 |
| 切换后网页打不开 | 默认路由残留或 DNS 配置错误 | 执行route print -4检查默认路由;重新设置 DNS;ipconfig /flushdns |
| 远程连接突然断开 | 脚本禁用了当前正在使用的网卡 | 不要禁用远程会话所在的网卡;改用另一张网卡地址重新连接 |
| PowerShell 提示禁止运行脚本 | 执行策略限制 | 执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser |
| 杀毒软件拦截 netsh/route | 安全软件对系统命令敏感 | 添加白名单或改用权限更细分的 PowerShell 命令 |
6.2 我踩过的几个坑
第一个坑是批处理编码。中文系统下,如果脚本用 UTF-8 保存,运行时的中文提示会出现乱码。解决办法是把文件开头写成chcp 65001 >nul,同时用 UTF-8 编码保存;或者干脆保持系统默认的 GBK 编码,去掉chcp那行。两种方式都能用,最怕的是编码和代码页不匹配。
第二个坑是 DHCP 重新分配路由。有的环境里,无线网卡启用的瞬间会从路由器拿到默认网关,自动生成一条默认路由,即使你后面执行了route delete 0.0.0.0 mask 0.0.0.0,它可能在几秒后又冒出来。应对办法是脚本最后加一次验证,发现多余默认路由再删一次。
第三个坑是网卡名里有空格或者怪异字符,尤其是某些 USB 无线网卡,名字可能叫“Wireless LAN adapter WLAN 2”。在批处理里双引号括起来问题不大,但如果写进 PowerShell 命令里,一定要用变量统一管理,避免手滑打错。
6.3 进一步自动化:检测网络质量以后自动切换
脚本写好了,还可以更进一步。有些人希望“断开无线就自动切有线,有线断了就自动切无线”,这时候单个切换脚本是不够的,需要加一个检测循环。
思路很简单:脚本先 ping 当前主链路的网关,如果连续失败,就自动调用另一个模式的切换函数。在 PowerShell 里可以用一个while循环加Test-Connection实现,但注意不要写得太激进,否则网卡频繁切换反而会影响使用。以我的经验,每 30 秒检测一次,连续失败 3 次再触发切换,体验比较稳定。
这个方案适合对网络可用性要求不高的办公场景,如果业务系统依赖固定 IP 和固定网关,自动切换可能带来更多问题,还是手动点一下更可靠。
最后再分享一个很小的实战习惯:我在脚本开头都会加一句echo 当前时间: %date% %time%,窗口里留下执行痕迹。下次任何人问“你是不是切过网络”,只要把命令行历史或者日志拉出来,一眼就能确认。工具再自动化,也得保留可追踪的记录,这是团队协作里最值钱的一环。