2015-01-11 3 views
6

का उपयोग कर एकाधिक CSV फ़ाइलों को विलय करना हैलो मैं पावरहेल स्क्रिप्ट की तलाश में हूं जो सभी सीएसवी फ़ाइलों को एक निर्देशिका में एक टेक्स्ट फ़ाइल (.txt) में विलय करेगा। सभी सीएसवी फाइलों में एक ही शीर्षलेख होता है जो हमेशा प्रत्येक फ़ाइल की पहली पंक्ति में संग्रहीत होता है। इसलिए मुझे पहली फ़ाइल से हेडर लेना होगा, लेकिन बाकी फ़ाइलों में पहली पंक्ति छोड़ी जानी चाहिए। मैं बैच फ़ाइल ढूंढने में सक्षम था जो मुझे वही कर रहा है जो मुझे चाहिए, लेकिन मेरे पास एक ही निर्देशिका में 4000 से अधिक सीएसवी फाइलें हैं और नौकरी करने में 45 मिनट से ज्यादा समय लगता है।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 
) 

कोई सुझाव है कि पावरहेल स्क्रिप्ट कैसे बनाएं जो इस बैच कोड से अधिक कुशल होगा?

धन्यवाद।

जॉन

उत्तर

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 ($ _। पूर्णनाम))} – john50

+0

एक सेकंड lemme इस पर काम करते हैं। – 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 फ़ाइलों में शामिल होने के लिए हमेशा के लिए लेता है और 3 9 99 फाइलों से पहली पंक्ति छोड़ देता है। – john50

1

आपकी बैच फ़ाइल बहुत अक्षम है! यह एक (आप हैरान हो जाएगा :)

@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

क्या आपको लगता है कि यह आपके और ओपी के बीच के अंतर को समझाने के लिए लायक होगा? – Matt

+0

@ मैट - एसिनी ने काउंटर वेरिएबल और लॉजिक-चेकिंग की आवश्यकता को हटा दिया है, जिससे लूप के अंदर स्क्रिप्ट को कम करने की अनुमति मिलती है, जिससे इसे तेज़ बना दिया जाता है। – 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

आपके उत्तर के लिए धन्यवाद लेकिन परिणाम.txt फ़ाइल कुछ के लिए है कारण उचित प्रारूप में नहीं है। जब मैं एफ 4 दबाता हूं तो सबकुछ एक साथ रखा जाता है। साथ ही जब मैं एक फ़ाइल की F3 अंतिम पंक्ति दबाता हूं तो एक नई फ़ाइल की पहली पंक्ति के साथ विलय कर दिया जाता है। – john50

+0

प्रत्येक सीएसवी लाइन के बाद बस "न्यूलाइन" डालने के लिए कोड संपादित किया गया। –

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

प्रतिबंध के लिए कारण यह है कि रिक्त स्थान की एक श्रृंखला में अधिक बार रूपांतरण टैब, और 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

आप एक लाइनर आप एक Import-Csv को पाइप प्रत्येक सीएसवी और फिर तुरंत कि Export-Csv को पाइप कर सकते हैं के बाद कर रहे हैं। यह प्रारंभिक शीर्षलेख पंक्ति को बनाए रखेगा और शेष फ़ाइलों को शीर्षलेख पंक्तियों को बहिष्कृत करेगा। यह स्मृति में सभी को लोड करने के बजाय एक बार में प्रत्येक सीएसवी को संसाधित करेगा और फिर उन्हें अपने विलयित सीएसवी में डंप करेगा।

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

सर्वोत्तम उत्तर imho: लघु, सीधा, और काम करता है। – davidhigh

+0

मैं सहमत हूं, सबसे अच्छा जवाब। –

0

मैं पिछले समाधान काफी प्रदर्शन के मामले में बड़े सीएसवी-फ़ाइलों के लिए अक्षम पाया है, तो यहां एक performant विकल्प है।

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

इसके बाद, आप शायद कई सीएसवी-हेडर से छुटकारा पाने के लिए चाहते हैं:

यहाँ एक वैकल्पिक जो केवल फ़ाइलें जोड़ देती है।