当列表为空时保护 foreach 循环

当列表为空时保护 foreach 循环

使用 Powershell v2.0 我想要删除任何超过 X 天的文件:

$backups = Get-ChildItem -Path $Backuppath | 
                Where-Object {($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like "backup*")}

foreach ($file in $backups)
{
    Remove-Item $file.FullName;
}

但是,当 $backups 为空时,我得到:Remove-Item : Cannot bind argument to parameter 'Path' because it is null.

我试过了:

  1. 使用以下方法保护 foreachif (!$backups)
  2. 使用以下方式保护 Remove-Itemif (Test-Path $file -PathType Leaf)
  3. 使用以下方式保护 Remove-Itemif ([IO.File]::Exists($file.FullName) -ne $true)

这些似乎都不起作用,如果列表为空,推荐的防止进入 foreach 循环的方法会怎样?

答案1

使用 Powershell 3,该foreach语句不会重复,$null并且 OP 描述的问题不再出现。

来自Windows PowerShell 博客邮政新的 V3 语言功能

ForEach 语句不会迭代 $nullForEach statement does not iterate over $null

在 PowerShell V2.0 中,人们常常感到惊讶的是:

PS> foreach ($i in $null) { 'got here' }

got here

当 cmdlet 不返回任何对象时,通常会出现这种情况。在 PowerShell V3.0 中,您无需添加 if 语句来避免迭代 $null。我们会为您解决这个问题。

对于 PowerShell,$PSVersionTable.PSVersion.Major -le 2请参阅以下内容以获取原始答案。


你有两个选择,我主要使用第二个。

检查$backups是否$null。一个简单的If循环就可以检查是否$null

if ( $backups -ne $null ) {

    foreach ($file in $backups) {
        Remove-Item $file.FullName;
    }

}

或者

初始化$backups为空数组。这避免了“迭代空数组”问题的歧义你在上一个问题中问到

$backups = @()
# $backups is now a null value array

foreach ( $file in $backups ) {
    # this is not reached.
    Remove-Item $file.FullName
}

抱歉,我忘记提供一个集成您的代码的示例。请注意Get-ChildItem数组中包装的 cmdlet。这也适用于可以返回 的函数$null

$backups = @(
    Get-ChildItem -Path $Backuppath |
        Where-Object { ($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like "backup*") }
)

foreach ($file in $backups) {
    Remove-Item $file.FullName
}

答案2

我知道这是一篇旧帖子,但我想指出,cmdletForEach-Objec不会遇到与使用ForEach关键字相同的问题。因此,您可以将结果通过管道传输DIRForEach,然后使用 $_ 引用文件,例如:

$backups | ForEach{ Remove-Item $_ }

您实际上可以通过管道转发 Dir 命令本身,甚至避免分配变量,例如:

Get-ChildItem -Path $Backuppath | 
Where-Object {
             ($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and `
             (-not $_.PSIsContainer) -and ($_.Name -like "backup*")
             } |
ForEach{ Remove-Item $_ }

我添加了换行符以提高可读性。

我知道有些人喜欢 ForEach/In,因为它更易于阅读。但有时ForEach-Object可能会有点麻烦,尤其是当你嵌套时,因为很难跟踪引用$_。无论如何,对于像这样的小操作来说,它是完美的。许多人还声称它更快,但我发现这只是一点点。

答案3

我开发了一个解决方案,通过运行两次查询,一次获取文件,一次通过强制转换 get-ChilItem 返回数组来计数文件(事后将 $backups 强制转换为数组似乎不起作用)。
至少它按预期工作(性能不应该成为问题,因为文件永远不会超过十几个),如果有人知道单查询解决方案,请发布它。

$count = @(Get-ChildItem -Path $zipFilepath | 
                Where-Object {($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like $partial + "*")}).count;

if ($count -gt 0)
{
    $backups = Get-ChildItem -Path $zipFilepath | 
                Where-Object {($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like $partial + "*")};

    foreach ($file in $backups)
    {
        Remove-Item $file.FullName;
    }
}

答案4

使用以下内容评估数组是否包含任何内容:

if($backups.count -gt 0) { echo "Array has contents" } else { echo "Array is empty" }

如果变量不存在,Powershell 将简单地将其评估为 false,因此无需检查它是否存在。

相关内容