告诉 Powershell 等待 foreach 完成写入 txt 文件

Tell Powershell to wait for foreach to finish writing to txt file

我有一个 .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 文件完成写入?

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

如果您在 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)
    }
}

或者,在使用 Set-Content 重写文件之前关闭 StreamWriter,然后使用 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"