while循环不会产生管道输出

时间:2016-10-13 01:47:14

标签: powershell

似乎While循环不会产生可以在管道中继续的输出。我需要处理一个大(很多GiB)文件。在这个简单的例子中,我想提取第二个字段,对其进行排序,然后只获取唯一值。什么我不了解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

3 个答案:

答案 0 :(得分:2)

使它变得比它需要的复杂一点。您有一个没有标题的CSV。以下应该有效:

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

答案 1 :(得分:2)

纳西尔的解决方法看起来就像去了这里。

如果您想知道代码中出现了什么问题,答案是 while循环(和do / while / until循环)不要像PowerShell中的其他语句那样始终将值返回到管道(实际上这是真的,我将保留其中的示例,但向下滚动以了解它的真正原因#39;为你工作)。

ForEach-Object - 一个cmdlet,不是内置语言功能/声明;确实将对象返回到管道。

1..3 | % { $_ }

foreach - 陈述;确实会回来。

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

if / else - 声明;确实会回来。

if ($true) { 1..3 }

for - 陈述;确实会回来。

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

switch - 陈述;确实会回来。

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上节省很多。

答案 2 :(得分:0)

其他解决方案

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