如何防止 PowerShell 的 Measure-Object cmdlet 截断您的数据?

How to prevent PowerShell's Measure-Object cmdlet from truncating your data?

我正在尝试比较以下数据以获得最大的数字:

$UserDeets 

name                 lastLogon
----                 ---------
Frank Ti 132273694413991065
Frank Ti 132279742884182029
Frank Ti 132282196073500496
Frank Ti 132272912975826719
Frank Ti 132282144707771693
Frank Ti 132228790551703041

为此,我尝试使用内置的 'measure' 函数。这是我正在执行的代码

($UserDeets| measure -Property lastLogon -Maximum ).Maximum

结果如下

1.322821960735E+17

如您所见,尽管它返回的是正确数据,但它截断了最后几位数字。

有没有办法防止这种截断?

好的,我有办法解决这个问题。答案是不用'measure'。这是一个解决方法,但它得到了想要的答案。

首先我对数组进行了排序:

$UserDeets = ($UserDeets | Sort-Object -Property LastLogon)

最高的对象将在数组的末尾,可以这样获得:

$UserDeets[-1]

我认为您尝试通过检查多个 DC 来获取用户的最新登录,我通过此代码执行此操作(其中:$samacc - 当前用户,$controller - 所有 DC 主机名的列表。) :

$scriptblock={
    param($samacc,$controller)
    $result=@()
    foreach($cont in $controller){
    $RESULT=$result + (Get-ADUser -Server $cont -Identity $samacc -Properties lastlogon,whenchanged,displayname,title,company  | sort-object lastLogon -descending | select-object enabled,displayname,samaccountname,title,company, @{Name="lastLogon";Expression={[datetime]::FromFileTime($_.'lastLogon')}},whenchanged)
    }
    $result|Sort-Object -Descending -Property LastLogon|select -First 1
    }

这对我有用。将其从 64 位浮点数变回 64 位整数。但有些数字会略有偏差(.000000000000005 或 5/1 千万亿% 错误),如果是日期时间,则为 6 百万分之一秒(滴答)。

$jsontext = @'
[ { name: 'Frank Ti', lastLogon: 132273694413991065 },
  { name: 'Frank Ti', lastLogon: 132279742884182029 },
  { name: 'Frank Ti', lastLogon: 132282196073500496 },
  { name: 'Frank Ti', lastLogon: 132272912975826719 },
  { name: 'Frank Ti', lastLogon: 132282144707771693 },
  { name: 'Frank Ti', lastLogon: 132228790551703041 },
  { name: 'Frank Ti', lastLogon: 132282196073500499 },
  { name: 'Frank Ti', lastLogon:   9007199254740991 } ]
'@

$userdeets = $jsontext | convertfrom-json
[long]($userdeets | measure lastlogon -Maximum).maximum

132282196073500496

Jeroen Mostert 在对问题的评论中提供了关键指针:

不幸的是,从 PowerShell 7.0 开始,Measure-Object invariably converts input numbers[1] for its -Sum, -Maximum, -Minimum, -Average and -StandardDeviation operations to [double] (System.Double) 并将结果报告为该类型,这可能会导致精度损失。

您输入的数字是 [long] 类型,并且它们的值超过了可以在 [double] 中精确表示的最大整数,即 9007199254740991(您可以用[bigint]::pow(2, 53) - 1)

一种有效的解决方法是使用 LINQ (System.Linq.Enumerable.Max):

[Linq.Enumerable]::Max(
  [long[]] $UserDeets.lastLogon
)

请注意,为了让 PowerShell 能够调用具有具体类型的通用 .Max() 方法,需要显式 [long[]] 强制转换。

另一个效率较低,但更符合 PowerShell 惯用的解决方法 是使用 排序,类似于

# Sort the LastLogon property values, then grab the *last* one,
# which is the largest value.
($UserDeets.LastLogon | Sort-Object)[-1]

仅对 .lastLogon 值的数组而不是完整的输入对象进行排序,可以最大限度地减少创建重复的排序数组的概念上不必要的开销,因此最大值。可以确定价值。


[1] 请注意,对于 -Min-Max 非数字 输入也被接受,只要它们实现System.IComparable 接口,在这种情况下,输入将按原样保留,不会发生精度损失;例如,'c', 'b', 'a' | Measure-Object -Minimum[datetime]::now, [datetime]::now.AddDays(1) | Measure-Object -Maximum 工作正常,因为类型 [string][datetime] 都实现了 IComparable.