PowerShell ForEach - 正在使用的并行文件

PowerShell ForEach -parallel file in use

我有一个带有并行循环的简单工作流程,但我 运行 在写出结果时遇到错误。因为写输出是并行的。我收到错误:

The process cannot access the file because it is being used by another process.

这是我的脚本:

workflow Get-ServerServices 
{
    $srvlst = Get-Content C:\TEMP\srvlst.txt

    foreach -parallel ($srv in $srvlst)
    {
         Get-Service -PSComputerName $srv | Out-File c:\temp\test.txt -Append

    }

}

有什么想法吗?

根据变量创建 test.txt 文件名 - 这样就不会有冲突。

在该过程结束时收集所有单独的文件并创建一个包含所有结果的文件 - 然后作为清理的一部分删除单独的文件'

我建议写入临时文件。您可以执行类似以下代码的操作:

workflow Get-ServerServices 
{
    #Get the temp path of the context user
    $TempPath = Join-Path -Path $([System.IO.Path]::GetTempPath()) -ChildPath "ServerServices"
    New-Item -Path $TempPath -ItemType Directory # and create a new sub directory
    $srvlst = Get-Content C:\TEMP\srvlst.txt

    foreach -parallel ($srv in $srvlst)
    {
        $TempFileName = [System.Guid]::NewGuid().Guid + ".txt" #GUID.txt will ensure randomness
        $FullTempFilePath = Join-Path -Path $TempPath -ChildPath $TempFileName
        Get-Service -PSComputerName $srv | Out-File -Path $FullTempFilePath -Force #Write out to the random file
    }

    $TempFiles = Get-ChildItem -Path $TempPath
    foreach ($TempFile in $TempFiles) {
        Get-Content -Path $TempFile.FullName | Out-File C:\temp.txt -Append #concatenate all the files
    }

    Remove-Item -Path $TempPath -Force -Recurse #clean up
}

基本上你正在获取临时目录,附加一个新目录,在你的输出中添加一堆 GUID 命名的文本文件,将它们全部连接成一个,然后将它们全部删除

您可以使用缓冲区并在末尾输出缓冲区内容,以更简单的形式完成:

workflow Get-ServerServices 
{
    $srvlst = Get-Content C:\TEMP\srvlst.txt
    $out = @()

    foreach -parallel ($srv in $srvlst)
    {
         $WORKFLOW:out += (Get-Service -ComputerName $srv)

    }

    Out-File c:\temp\test.txt -InputObject $out

}

有关详细信息,请参阅 technet "Variables in Parallel and Sequence Statements"

从 Powershell 6 开始,不再使用带有 Foreach 对象的工作流,参数 pass 也已更改,按照下面的示例,看看是否有帮助。

# Default directory variable
$DefaultDir = "C:\temp\sample"

# Log file.
$LogFile = "$($DefaultDir)\Log\Log_$([DateTime]::Now.ToString("yyyyMMdd-HHmmss")).txt"

# Directory where all computers are listed.
$AllComputerFolder = "$($DefaultDir)\ListToCheck\pcs.txt"

# Scan the file that lists all machines to be scanned.
Get-Content $AllComputerFolder | ForEach-Object -Parallel { 
    
    # It will only scan for machines that are connected to the network.
    $TurnedOn = Test-Connection -BufferSize 8 -Count 1 -ComputerName $($_ -replace "\", "") -Quiet

    # Will scan only for machines that are connected
    IF($TurnedOn -eq $True){

        # Run your script, notice the new way of passing parameters within Foreach-object: $using:DefaultDir
        $Return = ."$using:DefaultDir\Script\script.ps1" $_        
        
        # Writes in file the return of the processing of each machine.
        Add-Content -Path "$using:LogFile" -Value $Return 
    }ELSE{
        # Writes the return of machines with error to file
        Add-Content -Path "$using:LogFile" -Value "$($_) : $([DateTime]::Now.ToString("yyyyMMdd-HHmmss")) : Machine turned-off or out of the network"  
    }  

# Number of simultaneous threads
} -ThrottleLimit 20