使用PowerShell将多个CSV文件合并为一个

时间:2015-01-11 22:45:33

标签: powershell csv batch-file

您好我正在寻找powershell脚本,它会将目录中的所有csv文件合并到一个文本文件(.txt)中。所有csv文件都有相同的标题,它总是存储在每个文件的第一行。所以我需要从第一个文件中获取头文件,但是在其余文件中应该跳过第一行。 我能够找到完全符合我需要的批处理文件,但是我在一个目录中有超过4000个csv文件,这需要45分钟以上才能完成这项工作。

@echo off
ECHO Set working directory
cd /d %~dp0
Deleting existing combined file
del summary.txt
setlocal ENABLEDELAYEDEXPANSION
set cnt=1
for %%i in (*.csv) do (
 if !cnt!==1 (
 for /f "delims=" %%j in ('type "%%i"') do echo %%j >> summary.txt
) else (
 for /f "skip=1 delims=" %%j in ('type "%%i"') do echo %%j >> summary.txt
 )
 set /a cnt+=1
 )

有关如何创建powershell脚本的建议,该脚本比这个批处理代码更有效吗?

谢谢。

约翰

11 个答案:

答案 0 :(得分:33)

这会将所有文件追加到一起读取一个:

get-childItem "YOUR_DIRECTORY\*.txt" 
| foreach {[System.IO.File]::AppendAllText
 ("YOUR_DESTINATION_FILE", [System.IO.File]::ReadAllText($_.FullName))}

# Placed on seperate lines for readability

如果您需要,这个会在每个文件条目的末尾添加一个新行:

get-childItem "YOUR_DIRECTORY\*.txt" | foreach
{[System.IO.File]::AppendAllText("YOUR_DESTINATION_FILE", 
[System.IO.File]::ReadAllText($_.FullName) + [System.Environment]::NewLine)}

跳过第一行:

$getFirstLine = $true

get-childItem "YOUR_DIRECTORY\*.txt" | foreach {
    $filePath = $_

    $lines =  $lines = Get-Content $filePath  
    $linesToWrite = switch($getFirstLine) {
           $true  {$lines}
           $false {$lines | Select -Skip 1}

    }

    $getFirstLine = $false
    Add-Content "YOUR_DESTINATION_FILE" $linesToWrite
    }

答案 1 :(得分:28)

如果您使用的是单行,则可以将每个csv传送到Import-Csv,然后立即将其传送到Export-Csv。这将保留初始标题行并排除其余文件标题行。它还将一次处理每个csv,而不是将所有csv加载到内存中,然后将它们转储到合并的csv中。

Get-ChildItem -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv .\merged\merged.csv -NoTypeInformation -Append

答案 2 :(得分:2)

您的批处理文件非常低效!试试这个(你会感到惊讶:)

@echo off
ECHO Set working directory
cd /d %~dp0
Deleting existing combined file
del summary.txt
setlocal
for %%i in (*.csv) do set /P "header=" < "%%i" & goto continue
:continue

(
   echo %header%
   for %%i in (*.csv) do (
      for /f "usebackq skip=1 delims=" %%j in ("%%i") do echo %%j
   )
) > summary.txt

这是一项改进

  1. for /f ... in ('type "%%i"')需要加载并执行cmd.exe才能执行type命令,将其输出捕获到临时文件中,然后从中读取数据,这是通过每个输入文件< / em>的。 for /f ... in ("%%i")直接从文件中读取数据。
  2. >>重定向打开文件,在结尾追加数据并关闭文件,这是通过每个输出*行* 完成的。 >重定向使文件始终保持打开状态。

答案 3 :(得分:1)

这在PowerShell中非常简单。

$CSVFolder = 'C:\Path\to\your\files';
$OutputFile = 'C:\Path\to\output\file.txt';

$CSV= @();

Get-ChildItem -Path $CSVFolder -Filter *.csv | ForEach-Object { 
    $CSV += @(Import-Csv -Path $_)
}

$CSV | Export-Csv -Path $OutputFile -NoTypeInformation -Force;

这种方法的唯一缺点是它确实解析了每个文件。它还会将所有文件加载到内存中,因此,如果我们要讨论的是每个100 MB的4000个文件,那么您显然会遇到问题。

使用System.IO.FileSystem.IO.StreamWriter可能会获得更好的效果。

答案 4 :(得分:1)

我发现以前的解决方案在性能方面对大型csv文件来说效率很低,所以这里有一个高性能替代

这是一个只需附加文件的替代方法:

cmd /c copy  ((gci "YOUR_DIRECTORY\*.csv" -Name) -join '+') "YOUR_OUTPUT_FILE.csv" 

此后,您可能希望摆脱多个csv-headers。

答案 5 :(得分:1)

尝试一下,它对我有用

Get-Content *.csv| Add-Content output.csv

答案 6 :(得分:1)

TestExecutionListener

答案 7 :(得分:0)

这是一个同样使用System.IO.File的版本,

$result = "c:\temp\result.txt"
$csvs = get-childItem "c:\temp\*.csv" 
#read and write CSV header
[System.IO.File]::WriteAllLines($result,[System.IO.File]::ReadAllLines($csvs[0])[0])
#read and append file contents minus header
foreach ($csv in $csvs)  {
    $lines = [System.IO.File]::ReadAllLines($csv)
    [System.IO.File]::AppendAllText($result, ($lines[1..$lines.Length] | Out-String))
}

答案 8 :(得分:0)

以下批处理脚本非常快。只要没有任何CSV文件包含制表符,并且所有源CSV文件的行数少于64k,它就能正常工作。

@echo off
set "skip="
>summary.txt (
  for %%F in (*.csv) do if defined skip (
    more +1 "%%F"
  ) else (
    type "%%F"
    set skip=1
  )
)

限制的原因是MORE会将标签转换为一系列空格,而重定向的MORE会挂起64k行。

答案 9 :(得分:0)

$pathin = 'c:\Folder\With\CSVs'
$pathout = 'c:\exported.txt'
$list = Get-ChildItem -Path $pathin | select FullName
foreach($file in $list){
    Import-Csv -Path $file.FullName | Export-Csv -Path $pathout -Append -NoTypeInformation
}

答案 10 :(得分:-2)

输入* .csv&gt;&gt;文件夹\ combined.csv