用于CSV修改的Slow Powershell脚本

时间:2013-06-26 09:48:47

标签: powershell csv

我正在使用powershell脚本将数据附加到一堆文件的末尾。 每个文件都是一个大约50Mb的CSV(比如200万行),大约有50个文件。

我正在使用的脚本如下所示:

$MyInvocation.MyCommand.path

$files = ls *.csv 

foreach($f in $files) 
{
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($f)
$year = $basename.substring(0,4)

Write-Host "Starting" $Basename

$r = [IO.File]::OpenText($f)
while ($r.Peek() -ge 0) {
    $line = $r.ReadLine()
    $line + "," + $year | Add-Content $(".\DR_" + $basename + ".CSV")
}
$r.Dispose()

}

问题是,它很慢。它需要大约12个小时才能通过它们。 它并不是非常复杂,所以我不希望它花费那么长时间才能运行。 我该怎么做才能加快速度?

2 个答案:

答案 0 :(得分:2)

逐行读取和写入文件可能有点慢。也许你的防病毒软件也会导致缓慢。使用Measure-Command查看脚本的哪些部分是慢速的。

作为一般建议,而是写一些大块而不是很多小块。您可以通过在StringBuilder中存储一些内容并将其内容附加到输出文件(例如,1000个已处理的行)来实现此目的。像这样,

$sb = new-object Text.StringBuilder # New String Builder for stuff
$i = 1 # Row counter
while ($r.Peek() -ge 0) {
    # Add formatted stuff into the buffer
    [void]$sb.Append($("{0},{1}{2}" -f $r.ReadLine(), $year, [Environment]::NewLine ) )

    if(++$i % 1000 -eq 0){ # When 1000 rows are added, dump contents into file
      Add-Content $(".\DR_" + $basename + ".CSV") $sb.ToString()
      $sb = new-object Text.StringBuilder # Reset the StringBuilder
    }
}
# Don't miss the tail of the contents
Add-Content $(".\DR_" + $basename + ".CSV") $sb.ToString()

答案 1 :(得分:0)

当有可以对对象进行处理的cmdlet时,不要进入.NET Framework静态方法并构建字符串。收集数据,添加年份列,然后导出到新文件。你也在做大量的文件I / O,这也会减慢你的速度。

这可能需要更多的内存。但它会立即读取整个文件,并立即写入整个文件。它还假定您的CSV文件具有列标题。但是,对于其他人来说,查看和理解究竟发生了什么更容易 (编写脚本以便可以阅读!)。

# Always use full cmdlet names in scripts, not aliases
$files = get-childitem *.csv;

foreach($f in $files) 
{
    #basename is a property of the file object in PowerShell, there's no need to call a static method
    $basename = $f.basename;
    $year = $f.basename.substring(0,4)

    # Every time you use Write-Host, a puppy dies
    "Starting $Basename";

    # If you've got CSV data, treat it as CSV data. PowerShell can import it into a collection natively.
    $data = Import-Csv $f;
    $exportData = @();
    foreach ($row in $data) {
# Add a year "property" to each row object
        $row |Add-Member -membertype NoteProperty -Name "Year" -Value $year;
# Export the modified row to the output file
        $row |Export-Csv -NoTypeInformation -Path $("r:\DR_" + $basename + ".CSV") -Append -NoClobber
    }
}