问题
当 wsl 版本切换到 2 后,内部是使用虚拟机来实现,所以无法使用 localhost 访问 windows 的资源,想要通过 url 相互访问就必须先去查双方的局域网 ip(觉得无所谓请跳过接下来的表演)
解决思路
向 windows 的 hosts 文件写入 wsl 的 ip,向 wsl 的 hosts 文件写入 windows 的 ip,然后双方就能通过域名访问彼此
遇到的坑
- 操作 windows 和 wsl 的 hosts 文件都需要各自的管理员权限(windows 的管理员权限无法操作 wsl 的 hosts 文件,反之亦然)
- wsl 和 windows 都无法获取到对方的 ip
- windows 的脚本使用 CRLF 格式保存,wsl 脚本使用 LF 格式保存
- wsl 解析 windows 文件时
- 因为 windows 文件是 gbk 格式,有几率乱码,使用 grep 时会报错,需要加上 -a 选项
- 解析 windows 保存的文件时要过滤掉 \r(windows 的换行符是 \r\n)
- wsl 无法 ping 通 windows 的 ip,所以也无法通过域名 ping 通,但不影响 curl 等工具使用
解决步骤
需要使用两个脚本,一个 bat 和一个 shell 来解决上述问题
- 使用 windows 的管理员权限执行 bat 脚本,然后 bat 脚本内部使用 wsl 的管理员权限执行 shell 脚本
- bat 脚本
- 进入到脚本所在目录(windows 管理员权限打开终端路径在 C 盘)
- 将 ipconfig 的响应写入一个临时文件(供后面 wsl 读取)
- 使用管理员权限执行 wsl 脚本
- 删除临时文件
- shell 脚本
- 解析上述产生的临时文件,获取 windows 的 ip,通过 ifconfig 获取 wsl 的 ip
- 将 windows 中之前存在的 wsl 的 ip 清除,wsl 一样操作
- 将新的 ip 写入对方的 hosts 文件
脚本
假定 wsl 的域名为 wsl,windows 的域名为 win,实现如下
sync-ip.bat
@REM 此脚本需要管理员权限执行!
@echo off
@REM 使用管理员权限执行时,默认路径是 C 盘,脚本在 E 盘,需要使用 /d 选项才能切到脚本的目录
cd /d %~dp0
@REM wsl 无法读取到 windows 的 ip,所以需要先临时写入一个文件,然后 wsl 去解析获取 windows 的 ip
ipconfig > win_ipconfig
@REM 执行 shell 脚本(需要管理员权限)
wsl -u root bash sync-ip.sh
@REM 删除临时文件
del win_ipconfig
pause
sync-ip.sh
#!/bin/bash
# domain's name
win_domain=win
wsl_domain=wsl
# hosts' path
win_hosts=/mnt/c/Windows/System32/drivers/etc/hosts
wsl_hosts=/etc/hosts
# other
log_prefix='>>>'
win_ipconfig=$(dirname $0)/win_ipconfig
function write_hosts_file() {
ip=$1
i=$2
hosts=$3
domain=$4
if [ $i -eq 0 ]; then
content="$ip\t$domain"
else
content="$ip\t${domain}_$i"
fi
echo -e $content >> $hosts
}
function print_host_file () {
hosts=$(eval echo '$'$2'_hosts')
echo -e "$log_prefix $1 $2's hosts\n"
cat $hosts | grep -v '^#' | sed 's/\r//g' | grep -v '^$'
echo ''
}
function sync_wsl_to_win() {
echo "$log_prefix write win's hosts"
print_host_file old win
# remove old wsl's ip to win's hosts
cat $win_hosts | grep -v $wsl_domain > $win_hosts
# write new wsl's ip to win's hosts
wsl_ips=($(ifconfig | grep 'inet.*broadcast' | awk -F ' ' '{print $2}'))
for ((i = 0; i < ${#wsl_ips[@]}; i++)); do
write_hosts_file ${wsl_ips[i]} $i $win_hosts $wsl_domain
done
print_host_file new win
}
function sync_win_to_wsl() {
echo "$log_prefix write wsl's hosts"
print_host_file old wsl
# remove old wsl's ip to wsl's hosts
cat $wsl_hosts | grep -v $win_domain > $wsl_hosts
# write new wsl's ip to wsl's hosts
win_ips=($(cat $win_ipconfig | grep -a IPv4 | awk -F ' ' '{print $NF}' | sed 's/\r//g' | grep -v '\.1$'))
for ((i = 0; i < ${#win_ips[@]}; i++)); do
write_hosts_file ${win_ips[i]} $i $wsl_hosts $win_domain
done
print_host_file new wsl
}
sync_wsl_to_win
sync_win_to_wsl