我有一个看起来像这样的文件:
a,1
b,2
c,3
a,4
b,5
c,6
(...重复 1,000 行)
我怎样才能把它转变成这个?
a,b,c
1,2,3
4,5,6
谢谢
我有一个看起来像这样的文件:
a,1
b,2
c,3
a,4
b,5
c,6
(...重复 1,000 行)
我怎样才能把它转变成这个?
a,b,c
1,2,3
4,5,6
谢谢
这是来自地狱的蛮力单线,可以做到:
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。