在 PowerShell 并行工作流中将输出导出到 CSV 时出现问题

Issue while exporting output to CSV in PowerShell Parallel Workflow

我正在使用下面的脚本来获取有关多个远程服务器的端口状态的详细信息。

Workflow Test-OpenPortWF
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position=0)]
        [String[]]$Target,

        [Parameter(Mandatory=$true, Position=1)]
        [int]$Port
    )

    If(Test-Path -Path C:\Temp\Results.csv -ErrorAction SilentlyContinue){ Remove-Item -Path C:\Temp\Results.csv -Force }
    If(Test-Path -Path C:\Temp\Report.csv -ErrorAction SilentlyContinue){ Remove-Item -Path C:\Temp\Report.csv -Force }

    foreach -parallel -throttle 50 ($t in $Target)
    {
        Sequence
        {
            $Out = Test-NetConnection -ComputerName $t -Port $Port -WarningAction SilentlyContinue | Select ComputerName,RemoteAddress,RemotePort,@{N="PortTestSucceeded"; E={$_.tcpTestSucceeded}}
            Start-Sleep -Milliseconds 100
            $Out | Export-Csv -Path C:\Temp\Results.csv -NoTypeInformation -Append 
        }
    }

    InlineScript
    {
        Import-Csv c:\Temp\Results.csv | Select ComputerName,RemoteAddress,RemotePort,PortTestSucceeded  | Export-Csv c:\Temp\Report.csv -NoTypeInformation
        Remove-Item c:\Temp\Results.csv -Force
        Write-Host "Execution completed! Check Report.csv for output."
    }
}


# Example use for multiple servers for one port 5985 and export results to CSV file.
# Assuming all target servers are found in c:\temp\Servers.txt (new line separated)
#
# PS C:\Temp> Test-OpenPortWF -Target (Get-Content .\Servers.txt) -Port 5985 

大部分情况下它都在工作,但它无法给出完整的结果,因为我们运行这是一个并行工作流,如果两个服务器同时完成处理,它将尝试写入结果一次为这两个服务器导致以下错误的 CSV 文件。 CSV 文件中缺少大约 6% 的结果:

Microsoft.PowerShell.Utility\Write-Error : The process cannot access the file 'C:\Temp\Results.csv' because it is being used by another process. At Test-OpenPortWF:54 char:54 + + CategoryInfo : NotSpecified: (:) [Write-Error], CmdletInvocationException + FullyQualifiedErrorId : System.Management.Automation.CmdletInvocationException,Microsoft.PowerShell.Commands.WriteErrorCommand + PSComputerName : [localhost]

我们如何解决这个问题?

因为你使用并行处理,当多个线程尝试输出到你的csv文件时可能会发生冲突(根据错误判断你运行进入文件锁)

相反,尝试输出到单个临时文件(具有唯一名称),最后将这些文件合并到一个报告中(并删除临时文件)

例如在foreach循环中添加一个计数器($x),每次迭代递增($x++),然后将结果输出到-Path "C:\Temp\Results_$x.csv"

您需要使用互斥锁来锁定文件I/O操作。修改您的序列:

    Sequence
    {
        $Out = Test-NetConnection -ComputerName $t -Port $Port -WarningAction SilentlyContinue | Select ComputerName,RemoteAddress,RemotePort,@{N="PortTestSucceeded"; E={$_.tcpTestSucceeded}}
        $mutex = New-Object System.Threading.Mutex $false, 'NetConnectionTest'
        $mutex.WaitOne() > $null;
        $Out | Export-Csv -Path C:\Temp\Results.csv -NoTypeInformation -Append 
        $mutex.ReleaseMutex();
    }