且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在powershell中转置数据

更新时间:2023-11-18 21:50:34

这里有一个来自地狱的蛮力单行程序可以做到这一点:

Here's a brute-force one-liner from hell that will do it:

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 运算符.

It's not what I'd call elegant but should get you by. This should copy/paste correctly as-is. However if you reformat it to what is shown above you will need put back-ticks after the last curly on both the Begin and Process scriptblocks. This script requires PowerShell 2.0 as it relies on the new -split operator.

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

This approach makes heavy use of the Foreach-Object cmdlet. Normally when you use Foreach-Object (alias is Foreach) in the pipeline you specify just one scriptblock like so:

Get-Process | Foreach {$_.HandleCount}

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

That prints out the handle count for each process. This usage of Foreach-Object uses the -Process scriptblock implicitly which means it executes once for each object it receives from the pipeline. Now what if we want to total up all the handles for each process? Ignore the fact that you could just use Measure-Object HandleCount -Sum to do this, I'll show you how Foreach-Object can do this. As you see in the original solution to this problem, Foreach can take both a Begin scriptblock that is executed once for the first object in the pipeline and a End scripblock that executes when there are no more objects in the pipeline. Here's how you can total the handle count using Foreach-Object:

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

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

Relating this back to the problem solution, in the Begin scriptblock I initialize some variables to hold the array of names and values as well as a bool ($hdr) that tells me whether or not the header has been output (we only want to output it once). The next mildly mind blowing thing is that I also declare a function (output) in the Begin scriptblock that I call from both the Process and End scriptblocks to output the current set of data stored in $names and $values.

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

The only other trick is that the Process scriptblock uses the -contains operator to see if the current line's field name has already been seen before. If so, then output the current names and values and reset those arrays to empty. Otherwise just stash the name and value in the appropriate arrays so they can be saved later.

顺便说一句,输出函数需要在变量上使用 global: 说明符的原因是,当嵌套范围修改在其范围外定义的变量时,PowerShell 会执行写入时复制"方法.然而,当我们真的希望在更高的范围内进行修改时,我们必须通过使用像 global: 或 script: 这样的修饰符来告诉 PowerShell.

BTW the reason the output function needs to use the global: specifier on the variables is that PowerShell performs a "copy-on-write" approach when a nested scope modifies a variable defined outside its scope. However when we really want that modification to occur at the higher scope, we have to tell PowerShell that by using a modifier like global: or script:.