Set-Content : 该进程无法访问文件 'C:\WINDOWS\system32\drivers\etc\hosts',因为它正被另一个进程使用

Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by another process

我有以下 PowerShell 脚本:

param([switch]$Elevated)

function Test-Admin
{
    $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
    $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

if ((Test-Admin) -eq $false)  {
    if ($elevated) {
        # tried to elevate, did not work, aborting
    } else {
        Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated ' -f ($myinvocation.MyCommand.Definition))
    }
    exit
}

function UpdateHosts {
    param ($hostName)

    Write-Host $hostName

    try {
        $strHosts = (Get-Content C:\WINDOWS\system32\drivers\etc\hosts -Raw)
        if([string]::IsNullOrEmpty($strHosts)) {
            Write-Error "Get-Content hosts empty"
            exit
        }
    } catch {
        Write-Error "Unable to read hosts file"
        Write-Error $_
        exit
    }

    try {
        $strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+ $hostName","$ipAddress $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
    } catch {
        Write-Error "Unable to write hosts file"
        Write-Error $_ 
        exit
    }
}

$ipAddress = "127.0.0.1"

UpdateHosts -hostName local.pap360.com

有时,当我 运行 它时,我会收到以下错误:

Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by another process.

当我在记事本中打开 C:\WINDOWS\system32\drivers\etc\hosts 时,它是空白的。 IE。我在里面的所有数据都被擦掉了。

我的问题是...如何防止这种情况发生?

就像如果 Set-Content 无法访问主机文件并写入它那么它如何能够擦除它的内容?为什么 catch 块不工作?

这是完整的错误:

Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by
another process.
At C:\path\to\test.ps1:36 char:92
+ ...  $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (C:\WINDOWS\system32\drivers\etc\hosts:String) [Set-Content], IOException
    + FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.SetContentCommand

我也不明白为什么会断断续续。是否有某些 Windows 进程每分钟打开一次主机文件 1 秒或类似的?

首先,检查您的防火墙或 AV 软件是否未限制对该文件的访问。 如果不是这种情况并且 'some' 其他进程当前正在锁定主机文件,也许在读取或写入文件之前添加一个测试可以帮助:

function Test-LockedFile {
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName', 'FilePath')]
        [ValidateScript({Test-Path $_ -PathType Leaf})]
        [string]$Path
    )
    $file = [System.IO.FileInfo]::new($Path)
    # old PowerShell versions use:
    # $file = New-Object System.IO.FileInfo $Path

    try {
        $stream = $file.Open([System.IO.FileMode]::Open,
                             [System.IO.FileAccess]::ReadWrite,
                             [System.IO.FileShare]::None)
        if ($stream) { $stream.Close() }
        return $false   # file is not locked
    }
    catch {
        return $true    # file is locked
    }
}

然后像这样使用:

function UpdateHosts {
    param ($hostName)

    Write-Host $hostName

    $path = 'C:\WINDOWS\system32\drivers\etc\hosts'

    # test if the file is readable/writable
    # you can of course also put this in a loop to keep trying for X times
    # until Test-LockedFile -Path $path returns $false.
    if (Test-LockedFile -Path $path) {
        Write-Error "The hosts file is currently locked"
    }
    else {
        try {
            $strHosts = (Get-Content $path -Raw -ErrorAction Stop)
            if([string]::IsNullOrEmpty($strHosts)) {
                Write-Error "Get-Content hosts empty"
                exit
            }
        } 
        catch {
            Write-Error "Unable to read hosts file:`r`n$($_.Exception.Message)"
            exit
        }

        try {
            $strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+\s+$hostName", "$ipAddress $hostName" | 
            Set-Content -Path $path -Force -ErrorAction Stop
        } 
        catch {
            Write-Error "Unable to write hosts file:`r`n$($_.Exception.Message)"
            exit
        }
    }
}