빈 목록 일 때 foreach 루프 보호


10

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. 와 foreach를 보호 if (!$backups)
  2. 제거 항목 보호 if (Test-Path $file -PathType Leaf)
  3. 제거 항목 보호 if ([IO.File]::Exists($file.FullName) -ne $true)

이들 중 어느 것도 작동하지 않는 것 같습니다. 목록이 비어있는 경우 foreach 루프가 입력되지 않도록 권장하는 방법은 무엇입니까?


@Dan-($ backups> 0) 및 (@ ($ backups) .count -gt 0)을 모두 시도했지만 파일이 없을 때 예상대로 작동하지 않습니다.
SteB

답변:


19

Powershell 3을 사용하면 foreach명령문이 반복되지 않고 $nullOP에 설명 된 문제가 더 이상 발생하지 않습니다.

로부터 의 Windows PowerShell 블로그 포스트 새로운 V3 언어 특징 :

ForEach 문은 $ null을 반복하지 않습니다.

PowerShell V2.0에서 사람들은 종종 다음에 놀랐습니다.

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

got here

이 상황은 cmdlet이 개체를 반환하지 않을 때 종종 발생합니다. PowerShell V3.0에서는 $ null을 반복하지 않도록 if 문을 추가 할 필요가 없습니다. 우리는 당신을 위해 그것을 처리합니다.

PowerShell $PSVersionTable.PSVersion.Major -le 2의 경우 원래 답변은 다음을 참조하십시오.


두 가지 옵션이 있습니다. 나는 주로 두 번째 옵션을 사용합니다.

확인 $backups하지 마십시오 $null. If루프 주위의 간단한 것이 아닌지 확인할 수 있습니다.$null

if ( $backups -ne $null ) {

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

}

또는

$backupsnull 배열로 초기화하십시오 . 이렇게하면 마지막 질문에서 "iterate empty array"문제의 모호성을 피할 수 있습니다 .

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

나는 첫 번째 것을 사용했고 (더 이해하기 쉽다), 두 번째 것을 작동시킬 수 없었습니다 (아마도 뭔가 잘못하고 있었을 것입니다).
SteB

@SteB 당신은 맞습니다. 제 예제는 잘 설명되어 있지 않지만 (아직도) 예제 코드를 포함하여 편집을 제공했습니다. 동작에 대한 자세한 설명은 Keith Hill의 블로그에있는이 게시물을 참조하십시오 . PowerShell 전문가 일뿐만 아니라 나보다 훨씬 뛰어난 작성자이기도합니다. Keith는 StackOverflow에 적극적으로 참여 하고 있습니다. PS (또는 PS에 관심이있는 사람)가 자신의 내용을 확인하도록 권장합니다.
jscott

2

이 게시물은 오래된 게시물이지만 ForEach-Object cmdlet에는 ForEach 키워드를 사용하는 것과 같은 문제가 발생하지 않습니다. 따라서 DIR의 결과를 ForEach로 파이프하고 $ _를 사용하여 파일을 참조하면됩니다.

$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가 약간 털이 나올 수 있습니다. 특히 $ _ 참조를 따르기가 어려워 중첩되어있는 경우 특히 그렇습니다. 어쨌든 이와 같은 작은 작업에는 완벽합니다. 많은 사람들은 또한 그것이 더 빠르다고 주장하지만 나는 그것이 단지 약간이라는 것을 알았습니다.


+1 그러나 Powershell 3 (2012 년 6 월 경)에서는 foreach더 이상 문이 들어 가지 않으므로 $nullOP에 설명 된 오류가 더 이상 발생하지 않습니다. Powershell 블로그 게시물 새 V3 언어 기능 에서 "ForEach 문이 $ null을 반복하지 않습니다" 섹션을 참조하십시오 .
jscott

1

쿼리를 두 번 실행하여 한 번 파일을 가져오고 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;
    }
}

1
의견에 설명하는 것보다 쉽기 때문에 게시물을 효율적으로 편집했습니다. 마음에 들지 않으면 되 돌리십시오.
Dan

@ Dan-Doh, 나는 그것을 발견하지 못했다고 믿을 수 없다.
SteB

0

배열에 내용이 있는지 평가하려면 다음을 사용하십시오.

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

변수가 존재하지 않으면 Powershell은 단순히 변수를 거짓으로 평가하므로 존재 여부를 확인할 필요가 없습니다.


if ($ backups.count -gt 0)를 추가하면 $ backups에 1 개의 항목이 있어도 루프 실행이 중지됩니다. 자체적으로도 $ backups.count는 아무것도 출력하지 않습니다.
SteB

@ SteB 아, 객체 유형이 데이터를 보유하고있는 모든 객체에 대해 count가 구현되지 않은 것 같습니다. 나는 스캔하고 그것이 배열이라고 가정했다.
Dan

$ count = @ ($ backups) .count; 거의 작동하지만 f ($ count -gt 0)가 true 인 경우 파일이없는 경우!
SteB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.