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

时间:2018-08-10 10:56:59

标签: powershell powershell-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:该进程无法访问   文件“ C:\ Temp \ Results.csv”,因为该文件正在由另一个文件使用   处理。在Test-OpenPortWF:54 char:54   +       + CategoryInfo:未指定:(:) [Write-Error],CmdletInvocationException       + FullyQualifiedErrorId:System.Management.Automation.CmdletInvocationException,Microsoft.PowerShell.Commands.WriteErrorCommand       + PSComputerName:[本地主机]

我们如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

由于使用并行处理,因此当多个线程尝试输出到csv文件时可能会发生冲突(根据错误判断,您遇到了文件锁)

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

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

答案 1 :(得分:1)

您需要使用互斥锁来锁定文件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();
    }