超过阈值时执行的 PowerShell 脚本
PowerShell script to execute if threshold exceeded
首先,很抱歉这么长 post - 我正在尝试详细说明!
我希望自动解决我发现的问题。我有一个工作人员,一旦 "working" 目录中的文件超过 100,000 个,它就会定期轰炸。预防性地我可以停止进程并将工作目录重命名为 "HOLD" 并创建新的工作目录以使其继续运行。然后我将文件从 HOLD 文件夹移回工作目录,一次一点点,直到它赶上。
我想做的是使用 2 个 PowerShell 脚本通过 Task Scheduler 自动执行整个过程。
----脚本 1----
条件如下:
- 如果工作目录中的文件数大于 60,000
我发现( [System.IO.Directory]::EnumerateFiles($Working)
比Get-ChildItem
快。
操作:
Stop-Service
服务 1、服务 2、服务 3
Rename-Item -Path "C:\Prod\Working\" -NewName "Hold"
或 "Hold1","2","3", 等..如果文件夹已经存在——我不特别在意编号,只要它是一致的,所以如果更容易让系统命名为 HOLD、HOLD(1)、HOLD(2) 等,或者在 HOLD 之后附加日期,那很好。
New-Item C:\Prod\Working -type directory
Start-Service
服务 1、服务 2、服务 3
---脚本 2----
条件:
- 如果工作目录中的文件数少于 50,000
操作:
- Move 5,000 files from HOLD* folder(s) -- 将 5k 文件从 HOLD 文件夹移至空,然后跳过空文件夹并开始从 HOLD1 移动文件。此过程应该是动态的,并会重复到下一个文件夹。
在它出现之前,我很清楚将文件从工作文件夹简单地移动到保留文件夹会更容易,但是文件的大小可能非常大,移动它们似乎总是需要更长。
非常感谢任何意见,我渴望看到一些可靠的答案!
编辑
这就是我对脚本 2 的看法 运行 - 由 Bacon 提供
#Setup
$restoreThreshold = 30000; # Ensure there's enough room so that restoring $restoreBatchSize
$restoreBatchSize = 500; # files won't push $Working's file count above $restoreThreshold
$Working = "E:\UnprocessedTEST\"
$HoldBaseDirectory = "E:\"
while (@(Get-ChildItem -File -Path $Working).Length -lt $restoreThreshold - $restoreBatchSize)
{
$holdDirectory = Get-ChildItem -Path $HoldBaseDirectory -Directory -Filter '*Hold*' |
Select-Object -Last 1;
if ($holdDirectory -eq $null)
{
# There are no Hold directories to process; don't keep looping
break;
}
# Restore the first $restoreBatchSize files from $holdDirectory and store the count of files restored
$restoredCount = Get-ChildItem $holdDirectory -File `
| Select-Object -First $restoreBatchSize | Move-Item -Destination $Working -PassThru |
Measure-Object | Select-Object -ExpandProperty 'Count';
# If less than $restoreBatchSize files were restored then $holdDirectory is now empty; delete it
if ($restoredCount -lt $restoreBatchSize)
{
Remove-Item -Path $holdDirectory;
}
}
第一个脚本可能如下所示:
$rotateThreshold = 60000;
$isThresholdExceeded = @(
Get-ChildItem -File -Path $Working `
| Select-Object -First ($rotateThreshold + 1) `
).Length -gt $rotateThreshold;
#Alternative: $isThresholdExceeded = @(Get-ChildItem -File -Path $Working).Length -gt $rotateThreshold;
if ($isThresholdExceeded)
{
Stop-Service -Name 'Service1', 'Service2', 'Service3';
try
{
$newName = 'Hold_{0:yyyy-MM-ddTHH-mm-ss}' -f (Get-Date);
Rename-Item -Path $Working -NewName $newName;
}
finally
{
New-Item -ItemType Directory -Path $Working -ErrorAction SilentlyContinue;
Start-Service -Name 'Service1', 'Service2', 'Service3';
}
}
按我现在的方式分配 $isThresholdExceeded
的原因是因为我们不关心文件的确切数量是多少,只关心它是高于还是低于该阈值。一旦我们知道已超过阈值,我们就不需要 Get-ChildItem
的任何进一步结果(或 [System.IO.Directory]::EnumerateFiles($Working)
的相同结果),因此优化 Select-Object
将终止管道达到阈值后的元素。在 SSD 上包含 100,000 个文件的目录中,我发现这比允许 Get-ChildItem
枚举所有文件快了近 40%(4.12 对 6.72 秒)。使用 foreach
或 ForEach-Object
的其他实现被证明比 @(Get-ChildItem -File -Path $Working).Length
.
慢
至于为 'Hold'
目录生成新名称,您可以在某处保存和更新标识符,或者只生成带有递增后缀的新名称,直到找到未使用的名称。我认为仅根据当前时间命名更容易。只要脚本每秒 运行 不超过一次,您就会知道该名称是唯一的,它们将像数字一样排序,此外它还会为您提供一些诊断信息(目录的时间被轮换了)免费。
这是第二个脚本的一些基本代码:
$restoreThreshold = 50000;
$restoreBatchSize = 5000;
# Ensure there's enough room so that restoring $restoreBatchSize
# files won't push $Working's file count above $restoreThreshold
while (@(Get-ChildItem -File -Path $Working).Length -lt $restoreThreshold - $restoreBatchSize)
{
$holdDirectory = Get-ChildItem -Path $HoldBaseDirectory -Directory -Filter 'Hold_*' `
| Select-Object -First 1;
if ($holdDirectory -eq $null)
{
# There are no Hold directories to process; don't keep looping
break;
}
# Restore the first $restoreBatchSize files from $holdDirectory and store the count of files restored
$restoredCount = Get-ChildItem -File -Path $holdDirectory.FullName `
| Select-Object -First $restoreBatchSize `
| Move-Item -Destination $Working -PassThru `
| Measure-Object `
| Select-Object -ExpandProperty 'Count';
# If less than $restoreBatchSize files were restored then $holdDirectory is now empty; delete it
if ($restoredCount -lt $restoreBatchSize)
{
Remove-Item -Path $holdDirectory.FullName;
}
}
如 while
循环之前的注释中所述,条件是确保 $Working
中的文件数至少与 $restoreThreshold
中的文件数相差 $restoreBatchSize
个这样如果 $restoreBatchSize
个文件被恢复,它就不会超过这个过程中的阈值。如果您不关心这一点,或者选择的阈值已经考虑到了这一点,您可以更改条件以与 $restoreThreshold
而不是 $restoreThreshold - $restoreBatchSize
进行比较。或者,保持条件不变并将 $restoreThreshold
更改为 55000
.
按照我编写循环的方式,每次迭代最多 $restoreBatchSize
个文件将从它找到的第一个 'Hold_*'
目录中恢复,然后是 $Working
中的文件数被重新评估。考虑到,据我了解,在该脚本外部的 $Working
中添加和删除文件并同时执行,这可能是最安全的方法,也是最简单的方法。您当然可以通过计算低于 $restoreThreshold
的程度并从一个或多个 'Hold_*'
目录执行必要数量的批量恢复来增强这一点,所有这些都在循环的一次迭代中完成。
首先,很抱歉这么长 post - 我正在尝试详细说明!
我希望自动解决我发现的问题。我有一个工作人员,一旦 "working" 目录中的文件超过 100,000 个,它就会定期轰炸。预防性地我可以停止进程并将工作目录重命名为 "HOLD" 并创建新的工作目录以使其继续运行。然后我将文件从 HOLD 文件夹移回工作目录,一次一点点,直到它赶上。
我想做的是使用 2 个 PowerShell 脚本通过 Task Scheduler 自动执行整个过程。
----脚本 1----
条件如下:
- 如果工作目录中的文件数大于 60,000
我发现( [System.IO.Directory]::EnumerateFiles($Working)
比Get-ChildItem
快。
操作:
Stop-Service
服务 1、服务 2、服务 3Rename-Item -Path "C:\Prod\Working\" -NewName "Hold"
或 "Hold1","2","3", 等..如果文件夹已经存在——我不特别在意编号,只要它是一致的,所以如果更容易让系统命名为 HOLD、HOLD(1)、HOLD(2) 等,或者在 HOLD 之后附加日期,那很好。New-Item C:\Prod\Working -type directory
Start-Service
服务 1、服务 2、服务 3
---脚本 2----
条件:
- 如果工作目录中的文件数少于 50,000
操作:
- Move 5,000 files from HOLD* folder(s) -- 将 5k 文件从 HOLD 文件夹移至空,然后跳过空文件夹并开始从 HOLD1 移动文件。此过程应该是动态的,并会重复到下一个文件夹。
在它出现之前,我很清楚将文件从工作文件夹简单地移动到保留文件夹会更容易,但是文件的大小可能非常大,移动它们似乎总是需要更长。
非常感谢任何意见,我渴望看到一些可靠的答案!
编辑
这就是我对脚本 2 的看法 运行 - 由 Bacon 提供
#Setup
$restoreThreshold = 30000; # Ensure there's enough room so that restoring $restoreBatchSize
$restoreBatchSize = 500; # files won't push $Working's file count above $restoreThreshold
$Working = "E:\UnprocessedTEST\"
$HoldBaseDirectory = "E:\"
while (@(Get-ChildItem -File -Path $Working).Length -lt $restoreThreshold - $restoreBatchSize)
{
$holdDirectory = Get-ChildItem -Path $HoldBaseDirectory -Directory -Filter '*Hold*' |
Select-Object -Last 1;
if ($holdDirectory -eq $null)
{
# There are no Hold directories to process; don't keep looping
break;
}
# Restore the first $restoreBatchSize files from $holdDirectory and store the count of files restored
$restoredCount = Get-ChildItem $holdDirectory -File `
| Select-Object -First $restoreBatchSize | Move-Item -Destination $Working -PassThru |
Measure-Object | Select-Object -ExpandProperty 'Count';
# If less than $restoreBatchSize files were restored then $holdDirectory is now empty; delete it
if ($restoredCount -lt $restoreBatchSize)
{
Remove-Item -Path $holdDirectory;
}
}
第一个脚本可能如下所示:
$rotateThreshold = 60000;
$isThresholdExceeded = @(
Get-ChildItem -File -Path $Working `
| Select-Object -First ($rotateThreshold + 1) `
).Length -gt $rotateThreshold;
#Alternative: $isThresholdExceeded = @(Get-ChildItem -File -Path $Working).Length -gt $rotateThreshold;
if ($isThresholdExceeded)
{
Stop-Service -Name 'Service1', 'Service2', 'Service3';
try
{
$newName = 'Hold_{0:yyyy-MM-ddTHH-mm-ss}' -f (Get-Date);
Rename-Item -Path $Working -NewName $newName;
}
finally
{
New-Item -ItemType Directory -Path $Working -ErrorAction SilentlyContinue;
Start-Service -Name 'Service1', 'Service2', 'Service3';
}
}
按我现在的方式分配 $isThresholdExceeded
的原因是因为我们不关心文件的确切数量是多少,只关心它是高于还是低于该阈值。一旦我们知道已超过阈值,我们就不需要 Get-ChildItem
的任何进一步结果(或 [System.IO.Directory]::EnumerateFiles($Working)
的相同结果),因此优化 Select-Object
将终止管道达到阈值后的元素。在 SSD 上包含 100,000 个文件的目录中,我发现这比允许 Get-ChildItem
枚举所有文件快了近 40%(4.12 对 6.72 秒)。使用 foreach
或 ForEach-Object
的其他实现被证明比 @(Get-ChildItem -File -Path $Working).Length
.
至于为 'Hold'
目录生成新名称,您可以在某处保存和更新标识符,或者只生成带有递增后缀的新名称,直到找到未使用的名称。我认为仅根据当前时间命名更容易。只要脚本每秒 运行 不超过一次,您就会知道该名称是唯一的,它们将像数字一样排序,此外它还会为您提供一些诊断信息(目录的时间被轮换了)免费。
这是第二个脚本的一些基本代码:
$restoreThreshold = 50000;
$restoreBatchSize = 5000;
# Ensure there's enough room so that restoring $restoreBatchSize
# files won't push $Working's file count above $restoreThreshold
while (@(Get-ChildItem -File -Path $Working).Length -lt $restoreThreshold - $restoreBatchSize)
{
$holdDirectory = Get-ChildItem -Path $HoldBaseDirectory -Directory -Filter 'Hold_*' `
| Select-Object -First 1;
if ($holdDirectory -eq $null)
{
# There are no Hold directories to process; don't keep looping
break;
}
# Restore the first $restoreBatchSize files from $holdDirectory and store the count of files restored
$restoredCount = Get-ChildItem -File -Path $holdDirectory.FullName `
| Select-Object -First $restoreBatchSize `
| Move-Item -Destination $Working -PassThru `
| Measure-Object `
| Select-Object -ExpandProperty 'Count';
# If less than $restoreBatchSize files were restored then $holdDirectory is now empty; delete it
if ($restoredCount -lt $restoreBatchSize)
{
Remove-Item -Path $holdDirectory.FullName;
}
}
如 while
循环之前的注释中所述,条件是确保 $Working
中的文件数至少与 $restoreThreshold
中的文件数相差 $restoreBatchSize
个这样如果 $restoreBatchSize
个文件被恢复,它就不会超过这个过程中的阈值。如果您不关心这一点,或者选择的阈值已经考虑到了这一点,您可以更改条件以与 $restoreThreshold
而不是 $restoreThreshold - $restoreBatchSize
进行比较。或者,保持条件不变并将 $restoreThreshold
更改为 55000
.
按照我编写循环的方式,每次迭代最多 $restoreBatchSize
个文件将从它找到的第一个 'Hold_*'
目录中恢复,然后是 $Working
中的文件数被重新评估。考虑到,据我了解,在该脚本外部的 $Working
中添加和删除文件并同时执行,这可能是最安全的方法,也是最简单的方法。您当然可以通过计算低于 $restoreThreshold
的程度并从一个或多个 'Hold_*'
目录执行必要数量的批量恢复来增强这一点,所有这些都在循环的一次迭代中完成。