如何在PowerShell中转置数据

时间:2009-11-15 04:57:25

标签: powershell transpose

我有一个看起来像这样的文件:
一,1个
B,2
C,3
一个,4
B,5
C,6
(...重复1000行)

如何将其转换为此?
A,B,C
1,2,3
4,5,6

由于

1 个答案:

答案 0 :(得分: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 scriptblock,这意味着它对从管道接收的每个对象执行一次。现在如果我们想要为每个进程总计所有句柄呢?忽略你可以使用Measure-Object HandleCount -Sum来执行此操作的事实,我将向您展示Foreach-Object如何执行此操作。正如您在此问题的原始解决方案中所看到的,Foreach可以同时获取为管道中的第一个对象执行一次的Begin脚本块和在管道中不再有对象时执行的End脚本块。以下是使用Foreach-Object总计句柄数的方法:

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

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

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

BTW输出函数需要在变量上使用global:说明符的原因是当嵌套作用域修改在其作用域之外定义的变量时,PowerShell执行“写时复制”方法。但是,当我们真的希望在更高的范围内进行修改时,我们必须告诉PowerShell使用像global:或script这样的修饰符:。