启动时使用 WSL IP 更新 Windows“主机”文件

启动时使用 WSL IP 更新 Windows“主机”文件

我在 Windows 10 上使用 WSL2。

我的发行版是 Debian。

我想为我的发行版设置一个静态 IP,但我发现无法为 WSL 做到这一点回答

然而我发现可能还存在其他的可能性。

是否可以使用 Powershell 脚本来更新 Windows 10 上的 etc 主机,并使用新的 WSL IP 指向自定义域名(如172.18.225.26 dev.local启动时一样)?并且不会在主机文件上留下任何以前的 WSL 配置。

我的主机文件当前如下所示:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#   127.0.0.1       localhost
#   ::1             localhost
# Added by Docker Desktop
192.168.8.101 host.docker.internal
192.168.8.101 gateway.docker.internal
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section


# Added by Custom Script For WSL Distro
172.18.225.26 dev.local
#End of section

我对 Powershell 脚本一无所知,非常感谢您的帮助。

答案1

感谢这个的帮助,我得到了我想要的东西回答,但我确实做了一些修改。

基本上,我创建了一个update_wsl_ip_to_domain.ps1文件并保存到此 PATH C:\Scripts\update_wsl_ip_to_domain.ps1,然后添加了以下脚本

#
# Powershell script for adding/removing/showing entries to the hosts file.
#
# Known limitations:
# - does not handle entries with comments afterwards ("<ip>    <host>    # comment")
#

$file = "C:\Windows\System32\drivers\etc\hosts"
$wsl_ip = (wsl -d debian hostname -I).trim()
$data = @('add','localwsl.com',$wsl_ip)

function add-host([string]$filename, [string]$hostname, [string]$ip) {
    remove-host $filename $hostname
    $ip + "`t`t" + $hostname | Out-File -encoding ASCII -append $filename
}

function remove-host([string]$filename, [string]$hostname) {
    $c = Get-Content $filename
    $newLines = @()

    foreach ($line in $c) {
        $bits = [regex]::Split($line, "\t+")
        if ($bits.count -eq 2) {
            if ($bits[1] -ne $hostname) {
                $newLines += $line
            }
        } else {
            $newLines += $line
        }
    }

    # Write file
    Clear-Content $filename
    foreach ($line in $newLines) {
        $line | Out-File -encoding ASCII -append $filename
    }
}

function print-hosts([string]$filename) {
    $c = Get-Content $filename

    foreach ($line in $c) {
        $bits = [regex]::Split($line, "\t+")
        if ($bits.count -eq 2) {
            Write-Host $bits[0] `t`t $bits[1]
        }
    }
}

try {
    if ($data[0] -eq "add") {

        if ($data.count -lt 3) {
            throw "Not enough arguments for add."
        } else {
            add-host $file $data[1] $data[2]
        }

    } elseif ($data[0] -eq "remove") {

        if ($data.count -lt 2) {
            throw "Not enough arguments for remove."
        } else {
            remove-host $file $data[1]
        }

    } elseif ($data[0] -eq "show") {
        print-hosts $file
    } else {
        throw "Invalid operation '" + $data[0] + "' - must be one of 'add', 'remove', 'show'."
    }
} catch  {
    Write-Host $error[0]
    Write-Host "`nUsage: hosts add <ip> <hostname>`n       hosts remove <hostname>`n       hosts show"
}

接下来,我update_wsl_ip_to_domain.cmd在与该文件相同的目录中创建了一个单独的文件ps1,并添加了以下命令。

PowerShell Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Powershell -File C:\Scripts\update_wsl_ip_to_domain.ps1
PowerShell Set-ExecutionPolicy Restricted

因此,为了让update_wsl_ip_to_domain.cmd文件在启动时运行,我创建了该目录的快捷方式C:\Users\<user_name>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\并授予其管理员权限。

最后,我还在update_wsl_ip_to_domain.cmd桌面上以管理员权限添加了该文件的另一个快捷方式,并手动运行该快捷方式,因为由于某种原因,前一个快捷方式并不总是在启动时运行。

更新

在我的Debian WSL发行版上,我考虑apache在端口 80 和nginx端口 81 上运行。

因此,为了给他们一个静态 IP 和域名,我编辑了我的update_wsl_ip_to_domain.ps1文件并在底部添加了以下代码。

#GIVING WSL NGINX A STATIC IP. IT RUNS AT PORT 81 ON MY DEBIAN
netsh interface portproxy add v4tov4 listenport=80 listenaddress=127.65.43.21 connectport=81 connectaddress=$wsl_ip

#GIVING WSL APACHE  A STATIC IP. IT RUNS AT PORT 80 ON MY DEBIAN
netsh interface portproxy add v4tov4 listenport=80 listenaddress=127.65.43.22 connectport=80 connectaddress=$wsl_ip

hosts然后我在 10 号对我的档案进行了下列输入Windows

127.65.43.21        localwslnginx.com

127.65.43.22        localwslapache.com

我相信这方面有很大的潜力,我可能能够用我的apache或在不同的端口上运行多个应用程序nginx

我会随时更新。

更新 2 (2021 年 12 月 28 日)

我已经能够运行多个应用程序,wsl每个应用程序都有其唯一的域名。因此,以下是我hosts在 Windows 上的文件:

127.65.43.21        localwslnginx.com

127.65.43.22        localwslapache.com

# Laravel Apps
127.65.43.22        evangrest.test

127.65.43.22        firstbarcodes.test

它们必须分别与 或localwslapache.com承担相同的静态 IP 。localwslnginx.comapachenginx

对于apache,我使用以下配置来运行我的laravel应用程序:

<VirtualHost *:80>
    DocumentRoot "/data/www/firstbarcodes/public"
    DirectoryIndex "index.php"
    ServerName firstbarcodes.test
    ServerAlias firstbarcodes.test    

    <Directory "/data/www/firstbarcodes">
       Options Indexes FollowSymLinks
       AllowOverride None
       Require all granted
    </Directory>
   
    <Directory "/data/www/firstbarcodes/public">
      Options Indexes FollowSymLinks MultiViews ExecCGI
      AllowOverride All
      Order allow,deny
      Allow from all
      Require all granted
    </Directory>
    
    #Alias /js /data/www/firstbarcodes/public/js
    #<Directory /data/www/firstbarcodes/public/js>
    #  Options Indexes FollowSymLinks MultiViews ExecCGI
    #  AllowOverride All
    #  Order allow,deny
    #  Allow from all
    #  Require all granted
    #</Directory>
</VirtualHost>

您可能还需要运行此sudo a2enmod rewrite命令apache

我想通过这种方式,我就能以某种方式创建一个黑客程序,为我的所有apachenginx应用程序提供一个静态 IP WSL

我接下来想要做的是让它在启动时自动WSL Debian启动。apachemariadbwindows

答案2

我也遇到了同样的问题,并找到了自己的解决方案。这就是解决方案。它缺少添加静态 IP netsh,但是具有其他一些功能 :)


我有几个 WSL 发行版。

对于每个域名,我都会MYWSLDISTRONAME-hostnames.txtscripts目录中创建一个名为的文件。
此文件仅包含域名:

app.ds.local
pma.ds.local
sandbox.ds.local
test.ds.local

一开始,我将这些域名添加到文件末尾hosts,仅一次,例如

127.0.0.1 app.ds.local
127.0.0.1 pma.ds.local
127.0.0.1 sandbox.ds.local
127.0.0.1 test.ds.local

如果我必须为我的发行版添加另一个域名,我会停止 WSL,然后只需将
another.ds.local行添加到MYWSLDISTRONAME-hostnames.txt并添加
127.0.0.1 another.ds.localhosts

每次我需要启动 WSL 发行版时,我都会运行以下脚本MYWSLDISTRONAME-start.ps1。它的作用是:

  • 启动/root/start_services.sh发行版内的脚本,启动必要的服务等(从而解决缺少的问题systemd
  • 获取正在运行的 WSL 发行版的 IP 地址
  • hosts将中的每个域名的IP 替换为新 IP MYWSLDISTRONAME-hostnames.txt。所有这些操作都是通过临时hosts文件完成的。
  • 检查真实hosts文件是否需要修改。如果是,则pwsh启动一个单独的请求管理员权限的程序。如果否,脚本只会显示一条注释。
$runningDirectory = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

$hostsFile        = "c:\windows\system32\drivers\etc\hosts"
$wslHostnamesFile = "$runningDirectory\MYWSLDISTRONAME-hostnames.txt"
$tmpFile          = "$runningDirectory\.new-hostnames.txt"
$distroName       = "MYWSLDISTRONAME"


# Start services inside the WSL
wsl -d $distroName -e /root/start_services.sh

# Get IP address of WSL distro
$wslIpAddr = wsl -d $distroName -- ip addr
$match = [System.Text.RegularExpressions.Regex]::Match($wslIpAddr, "(?<ip>(172|192\.168)\.[\d\.]*)\/")
$ip = $match.Groups["ip"]

Write-Host "$distroName is available at $ip"


# read hosts file and change the IPs
$host_file_contents = Get-Content $hostsFile -Encoding UTF8 -Raw
$hostnames = Get-Content $wslHostnamesFile -Encoding UTF8
foreach ($hostname in $hostnames) {
    $host_file_contents = [System.Text.RegularExpressions.Regex]::Replace($host_file_contents, "(\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b)\s+$hostname", "$ip`t$hostname") 
}

# Save hosts file
$host_file_contents | Set-Content -Path $tmpFile -Encoding UTF8 

# Compare two files to avoid unncecessary popups
$result = Compare-Object -ReferenceObject ((Get-Content $hostsFile).trim() -ne '') -DifferenceObject ((Get-Content $tmpFile).trim() -ne '')
if ($result)
{
    # Prepare and execute encoded command (note that we use 'pwsh' instead of 'powershell')
    $command = "Get-Content -Path ""$tmpFile"" | Set-Content -Path ""$hostsFile"""
    Write-Host "Updating hosts file with: $command"
    $encodedCmd = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
    Start-Process -FilePath pwsh.exe -Verb RunAs -ArgumentList "-encodedcommand $encodedCmd"
}
else 
{
    Write-Host "Hosts file is the same, modification is unnecessary"
}

Start-Sleep -Seconds 5

# Remove temporary file
Remove-Item $tmpFile

笔记:我真的很喜欢你的想法netsh,可能会把它添加到我的脚本中。

并且,为了完整起见,我还有MYWSLDISTRONAME-stop.ps1

wsl.exe --shutdown -d MYWSLDISTRONAME

我还.lnk为这两个.ps1文件添加了漂亮且独特的图标,以便轻松识别每个发行版 :)。
它们可以在没有管理员权限的情况下启动,脚本会在必要时请求它们。


编辑:更改(?<ip>172\.[\d\.]*)\/为,(?<ip>(172|192\.168)\.[\d\.]*)\/因为看起来 WSL2 也已经开始从该范围发出 IP (?)。[2022 年 2 月 17 日编辑]

答案3

使用 Windows 11 和当前的 wsl2,我发现移动DNS似乎运行良好,您无需编写 hosts 文件脚本。我的 WSL2 发行版是 ubuntu 22.04。

您可以使用 mDNS 来通告您的 wsl 主机的主机名(带后缀).local。如果您在 中为您的 wsl2 环境分配主机名/etc/wsl.conf,如下所示:

[network]
hostname=projects

您的Windows主机应该能够解析该名称projects.local

从 WSL 终端,你可以使用以下命令检查 wsl 是否正在公布其 IP 地址:

$ avahi-resolve -n "$(hostname).local"
projects.local  172.17.0.1

PS:我发现只要我在Windows中启用数据包转发(如图所示这里), 我甚至可以使用名称从连接到默认 vSwitch 的 HyperV VM 访问我的 wsl 主机<wsl hostname>.local

相关内容