Protege o loop foreach quando a lista está vazia

10

Usando o Powershell v2.0 Eu quero excluir qualquer arquivo com mais de X dias:

$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;
}

No entanto, quando $ backups está vazio, recebo: Remove-Item : Cannot bind argument to parameter 'Path' because it is null.

Eu tentei:

  1. Protegendo o foreach com if (!$backups)
  2. Protegendo o item de remoção com if (Test-Path $file -PathType Leaf)
  3. Protegendo o item de remoção com if ([IO.File]::Exists($file.FullName) -ne $true)

Nada disso parece funcionar, e se a maneira recomendada de impedir que um loop foreach seja inserido se a lista estiver vazia?

    
por SteB 13.12.2012 / 11:05

4 respostas

18

Com o Powershell 3, a instrução foreach não itera sobre $null e o problema descrito pelo OP não ocorre mais.

No Blog do Windows PowerShell , poste Novos recursos de idioma V3 :

ForEach statement does not iterate over $null

In PowerShell V2.0, people were often surprised by:

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

got here

This situation often comes up when a cmdlet doesn’t return any objects. In PowerShell V3.0, you don’t need to add an if statement to avoid iterating over $null. We take care of that for you.

Para o PowerShell, $PSVersionTable.PSVersion.Major -le 2 , consulte o seguinte para obter uma resposta original.

Você tem duas opções, eu uso principalmente o segundo.

Verifique $backups para não $null . Um simples If ao redor do loop pode verificar por não $null

if ( $backups -ne $null ) {

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

}

Ou

Inicialize $backups como uma matriz nula. Isso evita a ambigüidade do problema da "matriz vazia iterada" que você fez em seu último pergunta .

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

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

Desculpe, deixei de fornecer um exemplo para integrar seu código. Observe o cmdlet Get-ChildItem envolto na matriz. Isso também funcionaria com funções que poderiam retornar um $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
}
    
por 13.04.2017 / 14:14
2

Eu sei que este é um post antigo, mas gostaria de salientar que o cmdlet ForEach-Object não sofre o mesmo problema que usar a palavra-chave ForEach. Então você pode canalizar os resultados de DIR para ForEach e apenas referenciar o arquivo usando $ _, como:

$backups | ForEach{ Remove-Item $_ }

Você pode realmente encaminhar o próprio comando Dir pelo pipe e evitar atribuir a variável como:

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

Eu adicionei quebras de linha para facilitar a leitura.

Eu entendo algumas pessoas como o ForEach / In para facilitar a leitura. Às vezes, ForEach-object pode ficar um pouco difícil, especialmente se você estiver aninhando, já que fica difícil seguir a referência $ _. De qualquer forma, para uma pequena operação como essa é perfeita. Muitas pessoas também afirmam que é mais rápido, mas descobri que isso é apenas um pouco.

    
por 20.03.2015 / 12:40
1

Eu desenvolvi uma solução executando a consulta duas vezes, uma vez para obter os arquivos e uma vez para contar os arquivos, lançando o get-ChilItem para retornar uma matriz (lançando $ backups como uma matriz após o fato não parecer para trabalhar)

Pelo menos funciona como o esperado (o desempenho não deve ser um problema, pois nunca haverá mais de uma dúzia de arquivos), se alguém souber de uma solução de consulta única, poste-a.

$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;
    }
}
    
por 13.12.2012 / 13:01
0

Use o seguinte para avaliar se o array tem algum conteúdo:

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

Se a variável não existir, o Powershell irá simplesmente avaliá-la como falsa, portanto não é necessário verificar se ela existe.

    
por 13.12.2012 / 12:17

Tags