Объединение нескольких CSV файлов в один с помощью PowerShell

Здравствуйте я ищу сценарий 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, который был бы более эффективным, чем этот пакетный код?

спасибо.

Джон

9 ответов


Это добавит все файлы вместе, читая их одно за другим:

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
    }

Если вы после однострочного вы можете передать каждый csv в Import-Csv а затем немедленно передайте это в Export-Csv. Это сохранит начальную строку заголовка и исключит оставшиеся строки заголовка файлов. Он также будет обрабатывать каждый csv по одному, а не загружать все в память, а затем сбрасывать их в объединенный csv.

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

Это довольно тривиально в 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;

единственным недостатком этого подхода является то, что он анализирует каждый файл. Он также загружает все файлы в память, поэтому если мы говорим о 4000 файлов, 100 МБ каждый, вы, очевидно, столкнетесь с проблемами.

вы можете получить лучшую производительность с System.IO.File и System.IO.StreamWriter.


ваш пакетный файл довольно неэффективен! Попробуйте этот (вы будете удивлены:)

@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, захватить его вывод во временный файл, а затем прочитать данные из него, и это делается с каждый входной файл. for /f ... in ("%%i") непосредственно считывает данные из файла.
  2. на >> редирект открывает файл, добавляет данные в конец и закрывает файл, и это делается с помощью каждый выход *линия*. The > перенаправление держит файл открытым все время.

Я нашел предыдущие решения довольно неэффективными для больших csv-файлов с точки зрения производительности, поэтому вот производительным альтернатива.

вот альтернатива, которая просто добавляет файлы:

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

после этого вы, вероятно, захотите избавиться от нескольких CSV-заголовков.


вот версия, также использующая систему.ИО.Файл,

$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))
}

следующий пакетный скрипт очень быстро. Он должен работать хорошо, пока ни один из ваших 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
  )
)

причина ограничений заключается в том, что больше преобразует вкладки в ряд пробелов и перенаправляет больше зависаний на 64K строк.


$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
}

тип *.csv > > папка\комбинированная.csv