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
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
}
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
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
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. >>
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.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
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.
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
.
Essayez ceci, cela a fonctionné pour moi
Get-Content *.csv| Add-Content output.csv
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))
}
$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
}
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.