2015-01-11 7 views
6

Здравствуйте, я ищу сценарий powershell, который объединил бы все файлы csv в каталоге в один текстовый файл (.txt). Все файлы csv имеют одинаковый заголовок, который всегда хранится в первой строке каждого файла. Поэтому мне нужно взять заголовок из первого файла, но в остальных файлах первая строка должна быть пропущена. Я смог найти командный файл, который делает именно то, что мне нужно, но у меня более 4000 файлов csv в одном каталоге, и для выполнения этой задачи требуется более 45 минут.Объединение нескольких CSV-файлов в один с помощью PowerShell

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

спасибо.

John

ответ

16

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

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 
    } 
+0

Этот код почти делает то, что мне нужно. И это довольно быстро, но мне нужно прочитать заголовок (первую строку) только из первого файла. Во всех остальных файлах первая строка должна быть пропущена. get-childItem. * .csv | foreach {[System.IO.File] :: AppendAllText (". \ summary.txt", [System.IO.File] :: ReadAllText ($ _. FullName))} – john50

+0

Работает над одним сексом. – kemiller2002

+0

Большое спасибо, отличная работа. – john50

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; 

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

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

+0

Благодарим вас за ответ. Не могли бы вы предложить, как внедрить System.IO.File и System.IO.StreamWriter в ваш код, потому что навсегда требуется присоединиться к 4000 файлам и пропустить первую строку из 3999 файлов. – john50

1

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

@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 

Как это Improvment

  1. for /f ... in ('type "%%i"') требует, чтобы загрузить и выполнить cmd.exe, чтобы выполнить команду типа, захватить его вывод во временном файле, а затем чтение данных из него, и это делается с помощью каждого входного файла. for /f ... in ("%%i") непосредственно считывает данные из файла.
  2. Переадресация >> открывает файл, добавляет данные в конец и закрывает файл, и это делается с помощью каждый вывод * строка *. Переадресация > сохраняет файл открытым все время.
+0

Считаете ли вы, что стоило бы объяснить разницу между вашими и OP? – Matt

+0

@Matt - Aacini устраняет необходимость в переменной счетчика и проверяет логику, давая скрипту меньше вещей, которые нужно делать внутри цикла, делая его быстрее. – SomethingDark

+0

Благодарим вас за помощь, но по какой-то причине она не работает. Ужасно это: «Удаление не распознается как внутренняя или внешняя команда, оперативная программа или командный файл. Я предполагаю, что должна существовать команда ECHO до« Удаление существующего объединенного файла «Но это не работает даже после того, как я его исправил. Всего есть несколько символов в сводном файле. – john50

1

Вот версия также с помощью 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)) 
} 
+0

Спасибо за ваш ответ, но файл result.txt для некоторых причина не в правильном формате. Когда я нажимаю F4, все объединяется. Также, когда я нажимаю F3, последняя строка одного файла объединяется вместе с первой строкой нового файла. – john50

+0

Только что отредактировал код, чтобы вставить «NewLine» после каждой строки csv. –

+0

Большое спасибо. Теперь он работает отлично, но это более чем в 2 раза медленнее, чем код Кевина. Если у кого-то больше нескольких сотен файлов в каталоге, это не имеет значения. Еще раз спасибо. – john50

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 преобразует вкладки в серии пространств и перенаправляется ПОДРОБНЕЕ зависаний на 64k линии.

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

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

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

лучший ответ imho: короткий, простой и работает. – davidhigh

+0

Согласен, лучший ответ. –

0

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

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

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

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

Смежные вопросы