2016-10-13 35 views
3

While循環不會產生可以在流水線中繼續的輸出。我需要處理一個大的(很多GiB)文件。在這個微不足道的例子中,我想提取第二個字段,對它進行排序,然後只獲取唯一值。我對While循環沒有什麼瞭解,並通過管道推動事物?While循環不產生流水線輸出

在* NIX世界,這將是一個簡單的:

cut -d "," -f 2 rf.txt | sort | uniq 

在PowerShell中,這將是不是很簡單。

源數據。

PS C:\src\powershell> Get-Content .\rf.txt 
these,1,there 
lines,3,paragraphs 
are,2,were 

腳本。

PS C:\src\powershell> Get-Content .\rf.ps1 
$sr = New-Object System.IO.StreamReader("$(Get-Location)\rf.txt") 

while ($line = $sr.ReadLine()) { 
    Write-Verbose $line 
    $v = $line.split(',')[1] 
    Write-Output $v 
} | sort 

$sr.Close() 

輸出。

PS C:\src\powershell> .\rf.ps1 
At C:\src\powershell\rf.ps1:7 char:3 
+ } | sort 
+ ~ 
An empty pipe element is not allowed. 
    + CategoryInfo   : ParserError: (:) [], ParseException 
    + FullyQualifiedErrorId : EmptyPipeElement 

回答

2

使它變得比它需要更復雜一點。你有一個沒有標題的CSV。以下應該工作:

Import-Csv .\rf.txt -Header f1,f2,f3 | Select-Object -ExpandProperty f2 -Unique | Sort-Object 
+0

將導入-CSV表現得像獲取內容,並嘗試將整個文件讀入內存?如果是這樣,那將不會處理大文件。 – lit

+0

@Liturgist我的理解是'Get-Content'(當沒有'-Raw'開關時調用)不會將整個文件讀入內存。如果你看到了這種行爲,那很可能是因爲你把它管道到'Sort-Object'('sort'是後者的別名)。看到我的答案爲什麼。 – briantist

+0

對延遲迴復表示歉意。 'Import-Csv'將一次讀取一行文件。你不需要擔心做一個單獨的'Get-Content'。 https://technet.microsoft.com/library/2a767ced-0fc9-4896-a8f0-2c5bdee49910(v=wps.630).aspx – Nasir

2

納西爾的解決方法看起來像這裏的方式。

如果你想知道在你的代碼是走錯了,答案是while環(和do/while/until環路)不一貫值恢復爲管道,在PowerShell中其他聲明的方式做(實際上這是真的,我會保留這些例子,但向下滾動的真正原因,它不適合你)。

ForEach-Object - 一個cmdlet,不是內置的語言特性/語句;將對象返回到管道。

1..3 | % { $_ } 

foreach - statement;確實會回來。

foreach ($i in 1..3) { $i } 

if/else - 聲明;確實會回來。

if ($true) { 1..3 } 

for - statement;確實會回來。

for ($i = 0 ; $i -le 3 ; $i++) { $i } 

switch - statement;確實會回來。

switch (2) 
{ 
    1 { 'one' } 
    2 { 'two' } 
    3 { 'three' } 
} 

但由於某些原因,這些其他循環似乎不可預知的行爲。

永遠循環,返回$i0;沒有遞增繼續)。

$i = 0; while ($i -le 3) { $i } 

沒有返回值,但$i沒有得到增加:

$i = 0; while ($i -le 3) { $i++ } 

如果你用小括號括裏面表情,似乎它得到的返回:

$i = 0; while ($i -le 3) { ($i++) } 

但事實證明(我在這裏學習了一下),while奇怪的返回語義有與你的錯誤無關;不管它們的返回值是什麼,你都不能將語句管理到函數/ cmdlet中。

foreach ($i in 1..3) { $i } | measure 

會給你同樣的錯誤。

您可以通過整個語句$()子表達式「繞過」這樣的:

$(foreach ($i in 1..3) { $i }) | measure 

將在此情況下,爲你工作。或者在你的while循環,而不是使用Write-Output,你可能只是你的項目添加到一個數組,然後對它進行排序後:

$arr = @() 

while ($line = $sr.ReadLine()) { 
    Write-Verbose $line 
    $v = $line.split(',')[1] 
    $arr += $v 
} 

$arr | sort 

我知道你在這裏處理一個大文件,所以也許你會想通過一行一行地管道到sort,你將避免大量的內存佔用。在許多情況下,管道在PowerShell中的工作方式是這樣的,但排序的方式是需要整個集合對其進行排序,因此Sort-Object cmdlet將「收集」您傳遞給它的每個項目,然後執行實際的排序結束;我不確定你完全可以避免這種情況。誠然,讓Sort-Object這樣做,而不是自己創建陣列可能會更高效,這取決於它的實現方式,但我認爲你不會在RAM上節省很多。

0

其他的解決辦法

Get-Content -Path C:\temp\rf.txt | select @{Name="Mycolumn";Expression={($_ -split ",")[1]}} | select Mycolumn -Unique | sort