告诉Powershell等待foreach完成向txt文件的写入操作

问题描述 投票:0回答:1

我有一个.ps1脚本,该脚本接收一些内部SQL数据并将其同步到Google API。

它通过对我们的数据运行foreach,使用逻辑生成API命令,然后通过System.IO.StreamWriter将这些命令写入文本文件来实现。然后,API处理器对该文件运行批处理作业。

我遇到的问题是,批处理作业部分似乎在foreach完成写入文件之前被触发,这会使整个脚本失败。

这是我的代码的简化版本:

$stream = [System.IO.StreamWriter] "C:\GAM\uploads\stream\gamBatch$today.txt";

## Loop csv and generate Google OU path
Write-Host "Generating location strings, OU paths, and update commands..."

Import-CSV "C:\uploads\Upload$today.csv" | ForEach-Object {

  ## Define fancy vars here
  ## Do fancy logic here

  ## Stream command list to log file
  $line = "update $deviceId assetid $Barcode location $location ou $ouPath";
  $stream.WriteLine($line);
};

## Trim top line of batch file (Removes header line)
(Get-Content "C:\uploads\stream\Batch$today.txt" | Select-Object -Skip 1) | Set-Content "C:\uploads\stream\Batch$today.txt";

## Close Stream instance and bulk run commands
Write-Host "Running batch command..."

$stream.WriteLine("commit-batch");
$stream.close();

apiBatch "C:\uploads\stream\Batch$today.txt";

这将在我的日志中生成此错误:

PS>TerminatingError(Set-Content): "The process cannot access the file 
'C:\uploads\stream\Batch2020-05-14.txt' because it is being used by another process."

我如何在启动批处理命令之前让Powershell等待txt文件写入完成?

powershell foreach batch-processing synchronous streamwriter
1个回答
0
投票

无需将第一行写入文件,仅需在之后立即将其再次删除(并且必须重写文件的其余部分)。

如果您在ForEach-Object循环中对它没有做任何有意义的事情,请立即跳过它:

Import-CSV "C:\uploads\Upload$today.csv" | Select-Object -Skip 1 | ForEach-Object {
    # ...
    $stream.WriteLine($line)
}

如果需要检查ForEach-Object主体内的第一行,请确保在第一次迭代中跳过调用WriteLine()

$first = $true
Import-CSV "C:\uploads\Upload$today.csv" | ForEach-Object {
    # ...
    if($first){
        $first = $false
    }
    else{
        $stream.WriteLine($line)
    }
}

或者,在用StreamWriter重写文件之前,先关闭Set-Content,然后使用Add-Content或新的流编写器来写最后一行:

Import-CSV "C:\uploads\Upload$today.csv" | ForEach-Object {
    # ...
    $stream.WriteLine($line)
}

# dispose of the current writer (will close the file stream)
$stream.Dispose()

... | Set-Content "C:\uploads\stream\Batch$today.txt"

# open a new writer and append the last string
$stream = (Get-Item "C:\uploads\stream\Batch$today.txt").AppendText()
$stream.WriteLine("commit-batch")

# or use `Add-Content`
"commit-batch" | Add-Content "C:\uploads\stream\Batch$today.txt"
© www.soinside.com 2019 - 2024. All rights reserved.