2016-10-13 32 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