超过阈值时执行的 PowerShell 脚本

PowerShell script to execute if threshold exceeded

首先,很抱歉这么长 post - 我正在尝试详细说明!

我希望自动解决我发现的问题。我有一个工作人员,一旦 "working" 目录中的文件超过 100,000 个,它就会定期轰炸。预防性地我可以停止进程并将工作目录重命名为 "HOLD" 并创建新的工作目录以使其继续运行。然后我将文件从 HOLD 文件夹移回工作目录,一次一点点,直到它赶上。

我想做的是使用 2 个 PowerShell 脚本通过 Task Scheduler 自动执行整个过程。

----脚本 1----

条件如下:

我发现( [System.IO.Directory]::EnumerateFiles($Working)Get-ChildItem快。

操作:

---脚本 2----

条件:

操作:

在它出现之前,我很清楚将文件从工作文件夹简单地移动到保留文件夹会更容易,但是文件的大小可能非常大,移动它们似乎总是需要更长。

非常感谢任何意见,我渴望看到一些可靠的答案!

编辑

这就是我对脚本 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 秒)。使用 foreachForEach-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_*' 目录执行必要数量的批量恢复来增强这一点,所有这些都在循环的一次迭代中完成。