将多个大型csv文件与Powershell

时间:2018-01-02 16:20:01

标签: powershell csv

对于如何使用Powershell将多个CSV文件合并为一个文件的问题有一些很好的回答,将标题行放在除this thread的第一个文件之外的所有文件上。
在大多数情况下,Kemiller2002发布的答案对我来说效果很好,但是当输出文件超过2GB时,我开始出现内存异常错误。抛出以下错误消息...

Exception of type 'System.OutOfMemoryException' was thrown.
At xxx.ps1:9 char:20
+            $false {$lines | Select -Skip 1}
+                    ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], OutOfMemoryException
    + FullyQualifiedErrorId : System.OutOfMemoryException  

这是使用Powershell 5.1。它似乎不是MaxMemoryPerShellMB(报告为2147483647)的问题,它似乎也不是实际系统内存的问题 - 我上次运行时有33GB的可用内存(总共64GB) )离开了。

现在脚本一直在运行,并且添加到文件中(我的一个最终文件大小约为7GB),但是当我看到它时,我无法确定它是否捕获了所有文件中的每一行错误弹出。

有什么建议吗?

修改

我添加了一些输出,因此我可以看到错误发生的位置。我附加了11个文件,大小从350 MB到1GB不等......这两个大约1GB的文件会导致错误。其中一个报告的长度为909,050,983,另一个报告的长度为973,429,260。

3 个答案:

答案 0 :(得分:1)

我没有大文件来测试它,但是使用.net方法可能是一种替代方法,因为你可以一次只处理1行而不是将整个内容加载到内存中。

$filepath = "c:\temp"
$outputfile = "c:\temp\output\result.csv"
$encoding = [System.Text.Encoding]::UTF8

$files = Get-ChildItem -Path $filePath -Filter *.csv

$w = New-Object System.IO.StreamWriter($outputfile, $true, $encoding)

$skiprow = $false
foreach ($file in $files)
{
    $r = New-Object System.IO.StreamReader($file.fullname, $encoding)
    while (($line = $r.ReadLine()) -ne $null) 
    {
        if (!$skiprow)
        {
            $w.WriteLine($line)
        }
        $skiprow = $false
    }
    $r.Close()
    $r.Dispose()
    $skiprow = $true
}

$w.close()
$w.Dispose()

答案 1 :(得分:0)

这简直是有些人使用这种方法进行此操作的方式...

Get-Content $SrcFile1, $SrcFile2 | Set-Content $DstFile

不要那样做!它非常缓慢,并且总是会导致内存异常错误。而是使用命令处理器中的旧文件副本,例如...

cmd /c "copy $($SrcFile1) + $($SrcFile2) $($DstFile)"

答案 2 :(得分:0)

从 Bob 提出的伟大观点演变而来的完整答案

<###########################
user config section
###########################>

# location of files to concatenate
$sourcefolder = "P:\DWH\ntm_v1\uncompressed"

# source file extension
$ext = "*.csv"

# output folder (best to have new folder for safety)
$outfolder = $sourcefolder + "\..\concatenated"

#output file name
$outfilename = "concat.txt"

<###########################
do work
###########################>

# build full path to out file
$concatfile = $outfolder + "\" + $outfilename

#create output folder
md -Force $outfolder

# delete output file if exists
if (Test-Path $concatfile) 
{
  Remove-Item -Confirm $concatfile
}

ForEach ($file in (Get-ChildItem -Path $sourcefolder -Filter $ext)) {
    $param = "type $file >> $concatfile"
    Write-Host "cmd /c $param"
        
    # run concat command
    cmd /c $param;  
}