Powershell:始终生成空文件(比较对象的输出)

Roc*_*ard 1 windows powershell compare file member-enumeration

此问题最流行的答案涉及以下 Windows powershell 代码(已编辑以修复错误):

$file1 = Get-Content C:\temp\file1.txt  
$file2 = Get-Content C:\temp\file2.txt   
$Diff = Compare-Object $File1 $File2  
$LeftSide = ($Diff | Where-Object {$_.SideIndicator -eq '<='}).InputObject  
$LeftSide | Set-Content C:\temp\file3.txt
Run Code Online (Sandbox Code Playgroud)

我总是得到一个零字节文件作为输出,即使我删除了 $Diff 行。

为什么输出文件总是为空,如何修复?

mkl*_*nt0 5

PetSerAl往常一样,在对该问题的评论中提供了关键的指针:

成员枚举-PSv3引入了访问集合上的成员(属性或方法)并将其隐式应用于其每个元素的能力,并将结果收集到数组

成员枚举不仅表达方便,而且比其他方法更快

一个简化的例子:

PS> ((Get-Item /), (Get-Item $HOME)).Mode
d--hs-   # The value of (Get-Item /).Mode
d-----   # The value of (Get-Item $HOME).Mode
Run Code Online (Sandbox Code Playgroud)

应用于-enclosed 命令输出.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.
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果收集的属性值本身是数组,则返回一个平面数组值:

      PS> @([pscustomobject] @{foo=1,2}, [pscustomobject] @{foo=3,4}).foo.Count
      4 # a single, flat array was returned: 1, 2, 3, 4
    
    Run Code Online (Sandbox Code Playgroud)

此外,成员枚举仅适用于获取(读取)属性值,而不适用于设置(写入)它们。 这种不对称是设计使然,以避免潜在的不需要的批量修改;在 PSv4+ 中,.ForEach('<property-name', <new-value>)用作最快的解决方法(见下文)。


这个方便的功能不可用,但是:

  • 如果您在PSv2 上运行(绝对)
  • 如果集合本身具有指定 name 的成员,则在这种情况下应用集合级别成员。

例如,即使在 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'
Run Code Online (Sandbox Code Playgroud)

在这种情况下 -通常在PSv2中 - 需要一种不同的方法:

  • 最快的替代方法,使用foreach statement,假设整个集合作为一个整体适合内存(使用成员枚举时暗示)。
PS> foreach ($s in 'abc', 'cdefg') { $s.Length }
3
5
Run Code Online (Sandbox Code Playgroud)
  • PSv4+ 的替代方案,使用集合方法 .ForEach(),也将集合作为一个整体进行操作:
PS> ('abc', 'cdefg').ForEach('Length')
3
5
Run Code Online (Sandbox Code Playgroud)

注意:如果适用于输入集合,您还可以使用设置属性值.ForEach('<prop-name>', <new-value>),这是无法使用 的最快解决方法.<prop-name> = <new-value>,即无法使用成员枚举设置属性值。

  • 最慢但内存高效的方法,使用管道

注意:如果您单独处理一个项目,而不将结果也收集到内存中,则使用管道只会节省内存。

使用ForEach-Object cmdlet,如Burt Harris 的有用回答

PS> 'abc', 'cdefg' | ForEach-Object { $_.Length }
3
5
Run Code Online (Sandbox Code Playgroud)

仅对于属性(而不是方法),Select-Object -ExpandProperty是一个选项;它在概念上清晰而简单,并且ForEach-Object在性能方面几乎与该方法相当(有关性能比较,请参阅我的这个答案的最后一部分):

PS> 'abc', 'cdefg' | Select-Object -ExpandProperty Length
3
5
Run Code Online (Sandbox Code Playgroud)

  • 你应该制作一本 powershell 字典(*我说 powershell 是因为某些含义在某些编程语言中意味着不同的东西*)。绝对会成为热销产品,我们很多人都会做广告并购买!我在很多这样的事情上遇到了困难,MS 文档可能有点令人困惑,而你的解释使它更容易理解。10/10 会买!@Santiago Squarzon,支持我......哈哈 (2认同)