这个问题最流行的答案涉及以下Windows powershell代码(为修复错误而编辑):
$file1 = Get-Content C:tempfile1.txt
$file2 = Get-Content C:tempfile2.txt
$Diff = Compare-Object $File1 $File2
$LeftSide = ($Diff | Where-Object {$_.SideIndicator -eq '<='}).InputObject
$LeftSide | Set-Content C:tempfile3.txt
我总是得到一个零字节文件作为输出,即使我删除了$Diff行。
为什么输出文件总是为空,如何修复?
PetSerAl,就像他经常做的那样,在对这个问题的评论中提供了关键的指针:
成员访问枚举 - 在PSv3中引入了访问集合上的成员(属性或方法)并将其隐式应用于其每个元素的能力,并将结果收集在数组中。[1]
成员访问枚举不仅富有表现力且方便,而且比其他方法更快。
一个简化的示例:
PS> ((Get-Item /), (Get-Item $HOME)).Mode
d--hs- # The value of (Get-Item /).Mode
d----- # The value of (Get-Item $HOME).Mode
将.Mode
应用于(...)
封闭命令输出的集合会导致在集合中的每个项上访问.Mode
属性,结果值以数组(常规 PowerShell 数组,类型为[System.Object[]]
)的形式返回。
警告:成员访问枚举像管道一样处理生成的数组,这意味着:
如果数组只有一个元素,则直接返回该元素的属性值,而不是在单元素数组中返回:
PS> @([pscustomobject] @{foo=1}).foo.GetType().Name Int32 # 1 was returned as a scalar, not as a single-element array.
如果收集的属性值本身是数组,则返回值的平面数组:
PS> @([pscustomobject] @{foo=1,2}, [pscustomobject] @{foo=3,4}).foo.Count 4 # a single, flat array was returned: 1, 2, 3, 4
此外,成员访问枚举仅适用于获取(读取)属性值,不适用于设置(写入)属性值。 这种不对称是设计使然,以避免潜在不必要的批量修改;在 PSv4+ 中,使用.ForEach('<property-name', <new-value>)
作为最快的解决方法(见下文)。
但是,此便捷功能不可用:
- 如果您在PSv2上运行(绝对)
- 如果集合本身具有指定名称的成员,则应用集合级别成员。
例如,即使在 PSv3+ 中,以下内容也不会执行成员访问枚举:
PS> ('abc', 'cdefg').Length # Try to report the string lengths
2 # !! The *array's* .Length property value (item count) is reported, not the items'
在这种情况下 - 以及一般的PSv2- 需要一种不同的方法:
- 最快的替代方法,使用
foreach
语句,假设整个集合作为一个整体放入内存中(使用成员访问枚举时隐含
)。
PS> foreach ($s in 'abc', 'cdefg') { $s.Length }
3
5
- PSv4+替代方案,使用收集方法
.ForEach()
,也对整个集合进行操作:
PS> ('abc', 'cdefg').ForEach('Length')
3
5
注意:如果适用于输入集合,您还可以使用.ForEach('<prop-name>', <new-value>)
设置属性值,这是无法使用.<prop-name> = <new-value>
的最快解决方法,即无法使用成员访问枚举设置属性值。
- 使用管道的最慢但内存效率高的方法:
注意:仅当您单独逐个处理项目而不在内存中收集结果时,管道的使用才具有内存效率。
使用ForEach-Object
cmdlet,如 Burt Harris 的有用答案:
PS> 'abc', 'cdefg' | ForEach-Object { $_.Length }
3
5
仅对于属性(与方法相反),Select-Object -ExpandProperty
是一个选项;它在概念上清晰而简单,在性能方面几乎与ForEach-Object
方法相当(有关性能比较,请参阅本答案的最后一部分):
PS> 'abc', 'cdefg' | Select-Object -ExpandProperty Length
3
5
[1] 以前,该功能半官方地称为成员枚举,在 2012 年的博客文章中与功能本身一起引入。2022 年初决定正式引入术语成员访问枚举。
也许而不是
$LeftSide = ($Diff | Where-Object {$_.SideIndicator -eq '<='}).InputObject
PowerShell 2 可能更适合:
$LeftSide = $Diff | Where-Object {$_.SideIndicator -eq '<='} |
Foreach-object { $_.InputObject }