web-dev-qa-db-fra.com

Fusion de plusieurs fichiers CSV en un seul à l'aide de PowerShell

Bonjour, je recherche un script powershell qui fusionnerait tous les fichiers csv d’un répertoire dans un fichier texte (.txt). Tous les fichiers CSV ont le même en-tête qui est toujours stocké dans une première ligne de chaque fichier. Je dois donc prendre l'en-tête du premier fichier, mais dans le reste des fichiers, la première ligne doit être ignorée ... J'ai pu trouver le fichier de commandes qui fait exactement ce dont j'ai besoin, mais j'ai plus de 4000 fichiers CSV dans un seul répertoire et le travail prend plus de 45 minutes.

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

Toute suggestion sur la façon de créer un script PowerShell qui serait plus efficace que ce code de lot?

Je vous remercie.

John

15
john50

Cela va annexer tous les fichiers ensemble en les lisant un à la fois: 

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

# Placed on seperate lines for readability

Celui-ci placera une nouvelle ligne à la fin de chaque entrée de fichier si vous en avez besoin: 

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

Ignorer la première ligne: 

$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

Si vous recherchez une ligne, vous pouvez diriger chaque csv vers un Import-Csv et le diriger immédiatement vers Export-Csv. Cela conservera la ligne d'en-tête initiale et exclura les autres lignes d'en-tête de fichiers. Il traitera également chaque csv un par un plutôt que de tout charger en mémoire puis de les transférer dans votre csv fusionné.

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

Votre fichier batch est plutôt inefficace! Essayez celui-ci (vous serez surpris :)

@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

Comment c'est une amélioration

  1. for /f ... in ('type "%%i"') nécessite de charger et d'exécuter cmd.exe afin d'exécuter la commande type, de capturer sa sortie dans un fichier temporaire, puis de lire les données qui y sont contenues, et cela avec chaque fichier d'entrée. for /f ... in ("%%i") lit directement les données du fichier. 
  2. La redirection >> ouvre le fichier, ajoute des données à la fin et ferme le fichier. Cette opération est effectuée avec chaque sortie * ligne *. La redirection > maintient le fichier ouvert tout le temps.
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

J'ai trouvé les solutions précédentes assez inefficaces pour les gros fichiers csv en termes de performances. Voici donc une alternative performant .

Voici une alternative qui ajoute simplement les fichiers:

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

Par la suite, vous voudrez probablement vous débarrasser des multiples en-têtes csv.

1
davidhigh

C'est assez trivial dans 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;

Le seul inconvénient de cette approche est qu’elle analyse tous les fichiers. Il charge également tous les fichiers en mémoire. Par conséquent, si vous parlez de 4 000 fichiers de 100 Mo chacun, vous aurez évidemment des problèmes.

Vous obtiendrez peut-être de meilleures performances avec System.IO.File et System.IO.StreamWriter.

1
Bacon Bits

Essayez ceci, cela a fonctionné pour moi

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

Voici une version utilisant également 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
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

Le script batch suivant est très rapide. Cela devrait bien fonctionner tant qu'aucun de vos fichiers CSV ne contient de caractères de tabulation et que tous les fichiers CSV sources comportent moins de 64 000 lignes.

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

La raison de ces restrictions est que MORE convertit les onglets en une série d'espaces et que MORE redirigé se bloque à 64 000 lignes.

0
dbenham