1

我有一个看起来像这样的文件:
a,1
b,2
c,3
a,4
b,5
c,6
(...重复 1,000 行)

我怎样才能把它转变成这个?
a,b,c
1,2,3
4,5,6

谢谢

4

1 回答 1

7

这是来自地狱的蛮力单线,可以做到:

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

这不是我所说的优雅,但应该让你通过。这应该按原样正确复制/粘贴。但是,如果您将其重新格式化为上面显示的内容,则需要在 Begin 和 Process 脚本块的最后一个卷曲之后加上反引号。此脚本需要 PowerShell 2.0,因为它依赖于新的 -split 运算符。

这种方法大量使用了 Foreach-Object cmdlet。通常,当您在管道中使用 Foreach-Object(别名为 Foreach)时,您只需指定一个脚本块,如下所示:

Get-Process | Foreach {$_.HandleCount}

打印出每个进程的句柄计数。Foreach-Object 的这种用法隐式使用 -Process 脚本块,这意味着它对从管道接收到的每个对象执行一次。现在如果我们想把每个进程的所有句柄加起来怎么办?忽略您可以Measure-Object HandleCount -Sum用来执行此操作的事实,我将向您展示 Foreach-Object 如何执行此操作。正如您在该问题的原始解决方案中看到的,Foreach 可以采用对管道中的第一个对象执行一次的 Begin 脚本块和在管道中没有更多对象时执行的 End 脚本块。以下是使用 Foreach-Object 计算句柄计数的方法:

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

将这与问题解决方案联系起来,在 Begin 脚本块中,我初始化了一些变量来保存名称和值的数组以及一个布尔值 ($hdr),它告诉我是否已经输出了标头(我们只想输出一次)。下一个有点令人兴奋的事情是,我还在 Begin 脚本块中声明了一个函数(输出),我从 Process 和 End 脚本块调用它来输出存储在 $names 和 $values 中的当前数据集。

唯一的另一个技巧是 Process 脚本块使用 -contains 运算符来查看当前行的字段名称是否已经被看到过。如果是这样,则输出当前名称和值并将这些数组重置为空。否则,只需将名称和值存储在适当的数组中,以便以后保存。

顺便说一句,输出函数需要在变量上使用 global: 说明符的原因是,当嵌套范围修改在其范围之外定义的变量时,PowerShell 会执行“写时复制”方法。但是,当我们真的希望修改发生在更高的范围内时,我们必须通过使用 global: 或 script: 之类的修饰符来告诉 PowerShell。

于 2009-11-15T07:34:22.547 回答