How to return current item back to the pipeline?

99封情书 提交于 2020-12-04 05:11:58

问题


Im currently importing csv files and bulk loading to sql table.

using this code

$CSVDataTable = Import-Csv $csvFile | % -begin {$i=0} -process { Write-Progress -activity "Importing file" -currentOperation "Reading line $i" -PercentComplete -1; $i++; return $_ } | Out-DataTable

i am able to show the progress, but I'd like to optimize it and one recommendation Ive found is utilizing StreamReader.

so ive tried the following:

[int]$LinesInFile = 0
$reader = New-Object IO.StreamReader $csvFile
$line = $reader.ReadLine()
while($reader.ReadLine() -ne $null) { $LinesInFile++ }

$CSVDataTable = 0..($LinesInFile-1) | foreach {
    $percent = ($_/$LinesInFile)*100
    Write-Progress -Activity 'Importing from CSV' -Status "$percent % Complete" -CurrentOperation "Importing row # $($_+1)" -PercentComplete $percent;
    return $reader[$_]
} | Import-Csv $csvFile | Out-DataTable

Error (due to return $reader[$_]):

Import-Csv : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its
properties do not match any of the parameters that take pipeline input.

回答1:


The only thing that makes sense to pipe to Import-Csv are System.IO.FileInfo instances representing CSV files, such as output by Get-ChildItem (which, as an aside, is broken due to a bug in Windows PowerShell, since fixed in PowerShell [Core] v6+).

If you want to report on the progress of an Import-Csv call, put a ForEach-Object command after it, in which you can emit the progress message and then pass Import-Csv's output object ($_) through:

# Count the data rows in the input CSV file.
$rowCount = 0
switch -File $csvFile { default { ++$rowCount } }
--$rowCount # subtract 1 from the line count to account for the header row.

Import-Csv $csvFile | ForEach-Object -Begin { $i = 0 } {

  $percent = '{0:N1}' -f (++$i / $rowCount * 100)
  Write-Progress -Activity 'Importing from CSV' -Status "$percent % Complete" -CurrentOperation "Importing row # $i" -PercentComplete $percent

  $_  # pass the object from Import-Csv through.

} | Out-DataTable

# Hide the progress bar now.
# (Otherwise it would linger until the script as a whole completes.)
Write-Progress '(unused))' -Completed

It's worth noting that per-object Write-Progress calls significantly slow down execution (and counting the number of lines up front has its cost too, as does even just passing objects through via a ForEach-Object call).

A simple way to mitigate the slowdown is to only use Write-Progress for every N objects, such as every 100 objects in the following example:

# Count the data rows in the input CSV file.
$rowCount = 0
switch -File $csvFile { default { ++$rowCount } }
--$rowCount # subtract 1 from the line count to account for the header row.

Import-Csv $csvFile | ForEach-Object -Begin { $i = 0 } {

  if (++$i % 100 -eq 1 -or $i -eq $rowCount) {
    $percent = '{0:N1}' -f ($i / $rowCount * 100)
    Write-Progress -Activity 'Importing from CSV' -Status "$percent % Complete" -CurrentOperation "Importing row # $i" -PercentComplete $percent
  }

  $_  # pass the object from Import-Csv through.

} | Out-DataTable

# Hide the progress bar now.
# (Otherwise it would linger until the script as a whole completes.)
Write-Progress '(unused))' -Completed

Note: If Out-DataTable requires a nontrivial amount of processing time after the last object has been passed to it, you could pad the number of rows for the purpose of the percentage display based on an estimate of that additional time, as a percentage of the true row count.



来源:https://stackoverflow.com/questions/64758238/how-to-return-current-item-back-to-the-pipeline

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!