powershell - Powershell 调用 Robocopy 以获取前 32 个最大文件的总大小不起作用
问题描述
下面的脚本是 DFS 设置和 Robocopy 命令的组合,用于列出文件服务器中前 32 个最大的文件。
我需要针对其他 10 个服务器执行以下代码,每个服务器都有大约3-4 百万个文件。
$results = Get-DfsrMembership | ForEach-Object {
$_ | Select-Object -Property `
@{ n = 'Server - IP'; e = { "$($_.ComputerName) [$((Resolve-DnsName -Name $_.ComputerName -Type A).IPAddress)]" } },
@{ n = 'Staging Path Quota GB'; e = { ($_.StagingPathQuotaInMB / 1000) } },
@{ n = 'Top 32 Largest Files Size'; e = { (robocopy /L /E /ndl /njh /njs /bytes $_.ContentPath nocopy | %{ if ($_ -match "New File\W*(\d*)\W*([\w:\\\.]*)") { [int64]$matches[1] } } | sort -Descending | select -first 32 | measure -sum | select -expand Sum) / 1gb } },
GroupName,
ContentPath,
State
}
$results | Sort-Object 'Top 32 Largest Files Size'
但是,上述脚本的结果缺少前 32 个最大文件大小列的值,因为它们都是 0。
当我执行此部分时:
服务器 A 中的 RDP 会话
(robocopy /L /E /ndl /njh /njs /bytes C:\DFS\Share nocopy | %{ if ($_ -match "New File\W*(\d*)\W*([\w:\\\.]*)") { [int64]$matches[1] } } | sort -Descending | select -first 32 | measure -sum | select -expand Sum) / 1gb }
服务器 B 中的 RDP 会话
(robocopy /L /E /ndl /njh /njs /bytes X:\DFS-Dir\Shared nocopy | %{ if ($_ -match "New File\W*(\d*)\W*([\w:\\\.]*)") { [int64]$matches[1] } } | sort -Descending | select -first 32 | measure -sum | select -expand Sum) / 1gb }
上面返回每个相应命令提示符 shell 上的数字。
解决方案
我认为您的意思是当您在 RDP 会话中运行它时它可以工作,但它在 PowerShell 代码中不起作用。
$noEmpties = [StringSplitOptions]::RemoveEmptyEntries
(( robocopy /L /E /ndl /njh /njs /np /nc /bytes C:\temp2 nocopy |
ForEach-Object{ [Int64]$_.Split(" `t", $noEmpties)[0] } |
Sort-object -Descending )[0..31] |
Measure-Object -Sum).Sum /1gb
以上是您所做工作的简化。它应该使用更少的管道和Select-Object
命令运行得更快一些。您可能还会考虑/MT:x
robocopy 参数。过去,我将日志记录结果与多线程混合,但是在测试这种情况时,它似乎可以工作。当然,如果性能是一个问题。
注意:我假设性能是一个问题,否则Get-ChildItem
编写起来会容易得多。
该$matches
方法有效,但阅读起来很复杂……我在 robocopy 命令中添加了/np
&/nc
以使解析也更容易一些。
现在当然它只会发出一个数字。该数字是最大的 32 个文件的总和。
我也不确定你需要第一个ForEach
,我想你可以直接进入Select-Object
命令......
如果您有超出此范围的问题,我认为您应该在表达式运行时查看表达式内部发生了什么。不同的结果可能是由于运行时的不同条件造成的,例如$_
可能不同。尝试在您的代码中放置一个断点或使用编辑器,并在您移动时逐步测试所有值和表达式。这可能有助于识别问题。
更新:
我没有 DFS 资源来测试您的确切场景,但我向您的原始代码提供了一个自定义对象并且它确实有效。
我使用相同的方法来测试我早期方法的含糖版本:
$noEmpties = [StringSplitOptions]::RemoveEmptyEntries
$Props =
@(
@{ n = 'Server - IP'; e = { "$($_.ComputerName) [$((Resolve-DnsName -Name $_.ComputerName -Type A).IPAddress)]" } }
@{ n = 'Staging Path Quota GB'; e = { ( $_.StagingPathQuotaInMB / 1000 ) } },
@{
n = 'Top 32 Largest Files Size'
e = {
( (Robocopy /L /E /NDL /NJH /NJS /NP /NC /Bytes C:\temp2 nocopy |
ForEach-Object{ [Int64]$_.Split(" `t", $noEmpties)[0] } |
Sort-object)[-1..-32] |
Measure-Object -Sum).Sum /1gb
}
}
'GroupName'
'ContentPath'
'State'
)
$results = Get-DfsrMembership |
Select-Object $Props |
Sort-Object 'Top 32 Largest Files Size'
这似乎奏效了。对于我自己的研究,我在执行主管道之前将表达式预制在一个数组中。那只是一种代码隔离方法。在这种情况下,提高可读性将在调试时大有帮助。使用你最喜欢的隔离方法;它可以很容易地移动到一个函数并从表达式中调用。
注意:您的原始表达式在我的测试中有效
在某一时刻,我确实返回了所有 0,这是因为我未能分配$noEmpties
给[StringSplitOptions]::RemoveEmptyEntries
. 这进一步让我觉得表达式中发生了一些意想不到的事情。我不能完全解决它,但如果它仍然是一个问题,你可以求助于调试。或者,如果我的样本在您的环境中存在同样的问题。
更新:
感谢您接受@Theo 的好答案,但我想指出一些事情。虽然我仍然不确定为什么某些远程条件会产生零,但我所有的测试都是本地的,所以你可以将我的表达式与 Theo 的Invoke-Command
方法一起使用。我提到的原因;我的方法具有复合性能优势。
当运行大约 5000 个文件时,Theo 的方法平均为 501 毫秒,而我的平均为 465 毫秒。在您提到的 3-4 百万个文件中,36 毫秒的其他微不足道的差异可能会相当复杂。
这不是我想出的最快方法,请查看:
$noEmpties = [StringSplitOptions]::RemoveEmptyEntries
[Int64[]]$Sizes =
Robocopy /L /E /NDL /NJH /NJS /NP /NC /BYTES C:\temp2 nocopy |
ForEach-Object{ $_.Split(" `t", $noEmpties)[0] }
[Int64[]]::Sort($Sizes)
(($Sizes[-1..-32] | Measure-Object -Sum).Sum) / 1gb
这真的很酷。通过类型约束数组,我将所有值强制为[Int64]
. 无需即时转换它们。然后我在数组类上使用了静态排序方法[Int[]]
,结果证明它比Sort-Object
. 我也确实找到了证实这一点的文件。我相信数组切片方法通常比 快,但我发现用任何类型的手动求和循环Select-Object
替换都没有优势。Measure-Object
注意:我怀疑该
.Split()
方法将有助于处理您的 其他问题。尽管也可能存在基于 RegEx 的方法。
现在在任何一种方法中,我都能够通过使用.SubString()
而不是拆分方法来获得更多的性能。这有点棘手,因为有些空白字符是制表符,有些是空格。
[Int64[]]$Sizes = Robocopy /L /E /NDL /NJH /NJS /NP /NC /Bytes C:\temp2 nocopy | ForEach-Object{ $_.Substring(0,14) }
[Int64[]]::Sort($Sizes)
(($Sizes[-1..-32] | Measure-Object -Sum).Sum) / 1gb
有几个看似随机的案例似乎不起作用,但总的来说它似乎可靠。如果有的话,您可能必须使用引用的字符串索引。该.split()
方法更可靠,但如果对性能角度感兴趣,我想添加此示例。
最后一件事;你实际上可以使用Get-ChildItem
((Get-ChildItem \\?\C:\temp2 -File -Recurse |
ForEach-Object{ $_.Length } |
Sort-Object)[-1..-32] |
Measure-Object -Sum).Sum/1gb
然而,在大约 5000 个文件的同一组中,平均大约 1230 毫秒的速度要慢得多。您可以在此处获得有关网络前缀语法的更多信息,\\?\
这里是示例。
推荐阅读
- batch-file - 批处理文件。迭代目录搜索排除一个
- javascript - 如何在javascript中执行查询
- python - 图像处理练习和 np.array 索引
- javascript - 反应中功能组件的早期回报
- javascript - 有条件地设置和清除间隔
- next.js - 如何在部署到 Vercel 的 Next js 中禁用目录列表
- spring-boot - 为什么 mvn spring-boot:run 找不到一些 ojdbc8 相关的 jar?
- javascript - 无法在 forEach 方法中返回 thisArg
- scala - sbt 返回依赖问题
- javascript - 考虑随时间的加速度和减速度的对象插值