web-dev-qa-db-de.com

Mehrere CSV-Dateien mithilfe von PowerShell zu einer zusammenführen

Hallo, ich suche ein Powershell-Skript, das alle CSV-Dateien in einem Verzeichnis in einer Textdatei (.txt) zusammenführen würde. Alle csv-Dateien haben denselben Header, der immer in einer ersten Zeile jeder Datei gespeichert wird. Ich muss also den Header aus der ersten Datei nehmen, aber in den restlichen Dateien sollte die erste Zeile übersprungen werden. Ich konnte eine Batch-Datei finden, die genau das tut, was ich brauche, aber ich habe mehr als 4000 CSV-Dateien in einem einzigen Verzeichnis, und die Arbeit dauert mehr als 45 Minuten.

@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
 )

Jeder Vorschlag, wie man ein Powershell-Skript erstellt, das effizienter ist als dieser Batch-Code?

Vielen Dank.

John

15
john50

Dies fügt alle Dateien zusammen und liest sie einzeln ein: 

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

# Placed on seperate lines for readability

Diese wird am Ende jedes Dateieintrags eine neue Zeile einfügen, wenn Sie diese benötigen: 

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

Überspringen der ersten Zeile: 

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

Wenn Sie auf der Suche nach einem Einzeiler sind, können Sie jede csv an einen Import-Csv weiterleiten und dann sofort an Export-Csv weiterleiten. Dadurch wird die erste Kopfzeile beibehalten und die verbleibenden Kopfzeilen der Dateien ausgeschlossen. Es wird auch jede csv nacheinander verarbeiten, anstatt alle in den Speicher zu laden und sie dann in die zusammengeführte csv zu speichern.

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

Ihre Batch-Datei ist ziemlich ineffizient! Versuchen Sie es hier (Sie werden überrascht sein :)

@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

Wie ist das eine Verbesserung

  1. Für for /f ... in ('type "%%i"') muss cmd.exe geladen und ausgeführt werden, um den type-Befehl auszuführen, seine Ausgabe in einer temporären Datei zu erfassen und Daten daraus zu lesen. Dies geschieht mit jeder Eingabedatei. for /f ... in ("%%i") liest direkt Daten aus der Datei. 
  2. Die >>-Umleitung öffnet die Datei, fügt die Daten am Ende hinzu und schließt die Datei. Dies geschieht mit jeder Ausgabe * Zeile *. Die >-Umleitung hält die Datei ständig geöffnet.
2
Aacini
Get-ChildItem *.csv|select -First 1|Get-Content|select -First 1|Out-File -FilePath .\input.csv -Force #Get the header from one of the CSV Files, write it to input.csv
Get-ChildItem *.csv|foreach {Get-Content $_|select -Skip 1|Out-File -FilePath .\Input.csv -Append} #Get the content of each file, excluding the first line and append it to input.csv
1
Randall Spies

Die bisherigen Lösungen waren für große CSV-Dateien in Bezug auf die Leistung ziemlich ineffizient, daher ist hier eine performante Alternative .

Hier ist eine Alternative, die die Dateien einfach anfügt:

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

Danach möchten Sie wahrscheinlich die mehreren CSV-Header loswerden.

1
davidhigh

Dies ist in PowerShell ziemlich trivial.

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

Der einzige Nachteil dieses Ansatzes ist, dass jede Datei analysiert wird. Außerdem werden alle Dateien in den Speicher geladen. Wenn Sie also über 4000 Dateien mit jeweils 100 MB sprechen, werden Sie offensichtlich auf Probleme stoßen.

Mit System.IO.File und System.IO.StreamWriter erzielen Sie möglicherweise eine bessere Leistung.

1
Bacon Bits

Versuchen Sie das, es hat bei mir funktioniert

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

Hier ist eine Version, die auch System.IO.File verwendet,

$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
Jan Chrbolka
$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
}
0
Dan Arseneau

Das folgende Batch-Skript ist sehr schnell. Es sollte gut funktionieren, solange keine Ihrer CSV-Dateien Tabulatorzeichen enthält und alle Quell-CSV-Dateien weniger als 64.000 Zeilen enthalten.

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

Der Grund für die Einschränkungen ist, dass MORE Registerkarten in eine Reihe von Leerzeichen konvertiert und MORE umgeleitet mit 64-KB-Zeilen hängt.

0
dbenham