2009-11-15 16 views
1

我有看起來像這樣一個文件:
一個,1個
B,2
C,3
一個,4
B,5
C,6
(...重複行數1000)如何用的powershell轉置數據

我該如何轉置它到這個?
A,B,C
1,2,3
4,5,6

感謝

回答

7

這裏有一個蠻力來自地獄的一行,將做到這一點:

PS> Get-Content foo.txt | 
     Foreach -Begin {[email protected]();[email protected]();$hdr=$false;$OFS=','; 
         function output { if (!$hdr) {"$names"; $global:hdr=$true} 
             "$values"; 
             $global:[email protected]();$global:[email protected]()}} 
       -Process {$n,$v = $_ -split ','; 
         if ($names -contains $n) {output}; 
         $names+=$n; $values+=$v } 
       -End {output} 
a,b,c 
1,2,3 
4,5,6 

這不是我所謂的優雅,但應該讓你。這應該按原樣正確複製/粘貼。但是,如果您將其重新格式化爲上面顯示的內容,則需要在「開始」和「進程」腳本塊上的最後一個卷標之後放置回刻度。此腳本需要PowerShell 2.0,因爲它依賴於新的-split操作符。

此方法大量使用Foreach-Object cmdlet。通常,當你在管道使用的foreach對象(別名是的foreach),你只需指定一個腳本塊,像這樣:

Get-Process | Foreach {$_.HandleCount} 

打印出每個進程的句柄計數。 Foreach-Object的這種用法隱式使用了-Process腳本塊,這意味着它爲從管道接收的每個對象執行一次。現在,如果我們想要爲每個過程總計所有句柄,該怎麼辦?忽略你可以用Measure-Object HandleCount -Sum這樣做的事實,我會告訴你Foreach-Object如何做到這一點。正如您在這個問題的原始解決方案中看到的,Foreach可以同時採用一次爲管道中第一個對象執行的Begin腳本塊和一個在管道中沒有更多對象時執行的EndScripblock。下面是如何使用的foreach對象總計中句柄計數:

gps | Foreach -Begin {$sum=0} -Process {$sum += $_.HandleCount } -End {$sum} 

與此回到問題的解決方案,在開始腳本塊我初始化一些變量來保存名稱和值的陣列,以及一個布爾( $ hdr),告訴我頭是否已經輸出(我們只想輸出一次)。下一個溫和的介意是,我還在Begin腳本塊中聲明瞭一個函數(輸出),我從Process和End腳本塊中調用它們來輸出存儲在$ names和$ values中的當前數據集。

唯一的另一個竅門是Process scriptblock使用-contains運算符來查看當前行的字段名稱是否已經在前面看過。如果是,則輸出當前名稱和值,並將這些數組重置爲空。否則,只需將名稱和值存儲在適當的數組中,以便以後可以保存它們。

順便說一句,輸出函數需要在變量上使用global:說明符的原因在於,當嵌套作用域修改在其作用域之外定義的變量時,PowerShell執行「寫入時複製」方法。然而,當我們真的希望在更高範圍內進行修改時,我們必須通過使用像global:或script:這樣的修飾符來告訴PowerShell。

+0

這些'\''甚至有必要嗎?從我所看到的那裏看到,它們中的很多都出現在塊中,而那些不需要告訴PowerShell它會在下一行繼續(因爲這可以從尚未完成的塊中推斷出來)。 – Joey 2009-11-15 10:51:24

+0

如果您取出反引號,則複製/粘貼到PowerShell控制檯的行爲不正確。 – 2009-11-15 20:05:13

+0

嗯,從SO複製/粘貼在預覽窗口和最後的帖子之間行爲不一樣。我剛剛使用預覽窗口進行了測試,這顯然是不夠的。在預覽窗口中,換行符通過複製/粘貼到控制檯,但它們不在最後發佈。什麼是PITA。 – 2009-11-16 00:14:34