リストが空のときに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. Remove-Itemの保護 if (Test-Path $file -PathType Leaf)
  3. Remove-Itemの保護 if ([IO.File]::Exists($file.FullName) -ne $true)

これらのどれも機能しないようですが、リストが空の場合にforeachループに入らないようにするための推奨される方法はどうでしょうか。


@Dan-($ backups> 0)と(@($ backups).count -gt 0)の両方を試しましたが、ファイルがない場合はどちらも期待どおりに機能しませんでした。
SteB

回答:


19

Powershell 3では、foreachステートメントは繰り返されず$null、OPによって記述された問題は発生しなくなりました。

Windows PowerShellのブログポスト新V3言語特徴

ForEachステートメントは$ nullを反復しません

PowerShell V2.0では、人々はしばしば次のことに驚きました。

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

got here

この状況は、コマンドレットがオブジェクトを返さない場合によく発生します。PowerShell V3.0では、$ nullの反復を避けるためにifステートメントを追加する必要はありません。私たちはあなたのためにそれを世話します。

PowerShell $PSVersionTable.PSVersion.Major -le 2については、元の回答については以下を参照してください。


あなたは2つのオプションを持っています、私は主に2番目を使用します。

$backupsないか確認してください$null。単純なIfループのループは、$null

if ( $backups -ne $null ) {

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

}

または

$backupsnull配列として初期化します。これにより、前回の質問で質問した「空の配列を繰り返す」問題のあいまいさが回避されます

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

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

申し訳ありませんが、コードを統合する例を提供するのを怠っていました。Get-ChildItem配列でラップされたコマンドレットに注意してください。これは、を返す可能性のある関数でも機能します$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番目のものを機能させることができませんでした(おそらく何か間違っていました)。
SteB

@SteB正解です。私の例は十分に説明されていません(まだ説明されていません)が、サンプルコードを含む編集を提供しました。動作の詳しい説明については、Keith Hillのブログのこの投稿を参照しください。彼はPowerShellのエキスパートであるだけでなく、私よりもはるかに優れたライターです。 キースはStackOverflowアクティブです。あなた(またはPSに興味がある人)に彼の作品をチェックすることをお勧めします。
jscott

2

私はこれが古い投稿であることを知っていますが、ForEach-Objectコマンドレットが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オブジェクトが少し毛むくじゃらになることがあります。とにかく、このような小さな操作には完璧です。多くの人はそれがより速いと主張しますが、私はそれがほんの少しだけであることを発見しました。


+1しかし、Powershell 3(2012年6月頃)では、foreachステートメントがにステップインしない$nullため、OPによって記述されたエラーは発生しなくなりました。Powershellブログの投稿V3言語の新機能の「ForEachステートメントは$ nullを反復しない」セクションを参照してください。
jscott 2015年

1

クエリを2回実行してソリューションを開発しました。1回はファイルを取得し、1回はget-ChilItemをキャストして配列を返すことでファイルをカウントします(事実が機能しないように思われる場合は$ backupsを配列としてキャストします)。 。
少なくとも期待どおりに機能します(ファイルが12を超えることはないため、パフォーマンスはそれほど問題になりません)。単一クエリソリューションを知っている人がいれば、投稿してください。

$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

@ダン-ドー、私はそれを見つけられなかったと信じられない、ありがとう。
SteB 2012

0

以下を使用して、配列にコンテンツがあるかどうかを評価します。

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

変数が存在しない場合、Powershellは単にfalseと評価するため、存在するかどうかを確認する必要はありません。


if($ backups.count -gt 0)を追加すると、$ backupsに項目が1つある場合でもループの実行が停止します。$ backups.countはそれ自体でも何も出力しません。
SteB

@SteBああ、データを保持しているオブジェクトタイプにはカウントが実装されていないと思います。私は読み取りをスキャンし、それが配列であると推定しました。
Dan

$ count = @($ backups).count; ほとんど動作しますが、f($ count -gt 0)がtrueの場合にファイルがない場合!
SteB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.