PowerShellを使用した効率的なファイル削除:Remove-ItemおよびWMI

サーバーやシステムを管理する際には、空きディスク容量を維持することが重要です。管理者としては、「ディスクフル」の状況に気付かないようにすることが望ましいです。確実に対応するために、PowerShellを使用してファイルを削除する方法を学ぶべきです。

本記事では、PowerShellを使用してシステムからファイルを削除する方法をほぼすべて紹介します。

さあ、始めましょう!

前提条件

この記事ではPowerShellを使用して例を示しており、一緒に進める予定の場合は、以下が必要です。

  • A computer that is running Windows 10 or above.
  • Windows PowerShell 5.1またはPowerShell 7.0
  • A script editor such as Visual Studio Code, Atom, or Notepad++.

ファイルを削除するためにRemove-Itemコマンドレットを使用する

ファイルを削除するために単純にPowerShellを使用する必要がある場合、おそらくすぐにRemove-Itemコマンドレットについて学ぶことでしょう。このコマンドレットは、PowerShellでファイルを削除するための事実上の標準です。

Remove-ItemGet-ChildItemコマンドレットと組み合わせてファイルとフォルダを読み取り、強力なPowerShellパイプラインを利用すると、作業が簡単になります。

関連記事:Get-ChildItem:ファイル、レジストリ、証明書などの一覧表示

Remove-Itemコマンドレットには、delという別名があることを知っていましたか?PowerShellで作業する際に、Remove-Itemまたはdelを使用すると同じコマンドが実行されます。

ファイルを削除するためにPowerShellを使用する

最も役立つ最初の例は、最も基本的なものです。つまり、単一のファイルを削除することです。単一のファイルを削除するには、以下のコマンドを使用するだけです。以下のコードは、ファイル C:\temp\random.txt を削除します。

Remove-Item -Path C:\temp\random.txt

このコードをPowerShellで実行すると、エラーが発生しない限り、画面には何も表示されません。

フォルダ内のすべてのファイルを削除するためにPowerShellを使用する

この例では、以下のコードはフォルダ内のすべてのファイルを削除します。 Get-ChildItem cmdletは、-Path パラメータを使用して C:\temp を対象にします。 -File パラメータは、ファイル のみを含めることを示します。 Get-ChildItem はフォルダを無視します。

Get-ChildItem -Path C:\temp -File | Remove-Item -Verbose

出力は以下のスクリーンショットのようになります。

Successfully deleted all files in a folder

PowerShellを使用してすべてのファイルを再帰的に削除する

前の例では、C:\temp フォルダ内のファイルのみが削除されました。すべてのサブディレクトリ内のファイルも削除する場合は、Get-ChildItem cmdletに -Recurse スイッチを追加してファイルを再帰的に取得する必要があります。

Get-ChildItem -Path C:\temp -File -Recurse | Remove-Item -Verbose

上記のコードを実行すると、PowerShellはすべてのサブフォルダを調べ、ファイルの一覧を取得します。以下の出力は、コードがトップフォルダ内のファイルを削除できたことを示していますが、サブフォルダ内のファイルを取得できなかったことを示しています。

Failed to retrieve files in sub-folders

長いパスの問題に対処する

上記のエラーは、PowerShellが 「パスの一部が見つかりませんでした」ということを示しています。このエラーは、cmdletがアクセスしようとしているパスが存在しないことを示しており、誤解を招くものです。

この場合、エラーは、Get-ChildItemが読み取ろうとしているパスが、260文字の最大パス長を超えているためです。

以下のスクリーンショットは、パスまたはディレクトリとそのサブディレクトリが存在し、1つのテキストファイルであるInTooDeep.txtが最も下位のサブディレクトリに存在していることを示しています。ネストされたディレクトリ名を構成するすべての文字の組み合わせが、長いパスの問題を引き起こしています。

Nested directories creating a long path name

Windows PowerShell 5.1では、長いパス名の問題に対する回避策があります。回避策は、パスのUnicodeバージョンを使用することです。パスを次のように指定する代わりに- C:\temp、このようにUnicodeバージョンを使用します- ‘\\?\C:\temp’(ローカルに位置するフォルダの場合)、または’\\?\UNC\<computername>\<share>\Temp’(フォルダがUNCパスに位置する場合)

Get-ChildItem -Path '\\?\C:\temp' -File -Recurse | Remove-Item -Verbose

長いパス名に対応した変更済みのコードを使用して、上記の出力は、PowerShellが深くネストされたパス名を正常に読み取り、ファイルを削除したことを示しています。

Deleted the file with a long path name

長いパス名の問題はPowerShell 7.0には影響しません。PowerShell 7.0では、長いパス名に対する組み込みサポートが既にあるため、Unicodeバージョンのパスを使用する必要はありません。

フォルダも削除する必要がある場合は、Get-ChildItemコマンドレットから-Fileパラメータを削除し、PowerShellはファイルとフォルダを含むすべてのアイテムを削除するはずです。

PowerShellを使用してx日より古いファイルを削除する

ディスクスペースの整理のもう一つの典型的な例として、特定の日数よりも古いファイルを削除することが挙げられます。この例では、IISウェブサーバーによって生成されるような古いログファイルを削除してディスクスペースを解放するのに役立ちます。

この例では、c:\tempに14日よりも古いファイルがあります。以下のスクリプトを使用すると、c:\tempの各ファイルのNameCreationTime、およびAgeInDaysが表示されます。

Get-ChildItem c:\temp | Select-Object Name,CreationTime,@{n='AgeInDays';e={(New-TimeSpan -Start $PSItem.CreationTime).Days}}

以下のスクリーンショットで確認できるように、15日前、7日前、および0日前のファイルがあります。

List of files in c:\temp

削除するファイルを把握したので、特定の日数よりも古いファイル(この場合は14日よりも古い)のみを削除するスクリプトを作成できます。

以下の例のスクリプトでは、C:\tempにあるCreationTimeの値が設定された閾値よりも古いファイルが削除されます。

最初の行では、Get-ChildItemが検索するパスを定義しています。パスは$path変数に保存されます。次に、2行目で閾値が指定されます。$thresholdの値は削除するファイルの年齢(日数)を表します。

$threshold変数の次のコード行では、以下のことを行います:

  • $path変数で指定されたフォルダにあるファイルのコレクションを取得します。この例では、パスはC:\tempです。
  • $threshold変数に保存された日数よりもCreationTimeの値が古いファイルのみを出力に含めます。この例では、閾値は14日です。
  • フィルタリングされたファイルのリストをRemove-Itemにパイプして、それらのファイルを削除します。
$path = 'C:\Temp'
$threshold = 14
Get-ChildItem -Path $path -File | Where-Object {$PSItem.CreationTime -lt (Get-Date).AddDays(-$threshold)} |Remove-Item -Verbose

上記のコードを実行すると、以下のような出力が表示されます。

The script deleted the files older than 14 days

上のスクリーンショットからわかるように、詳細メッセージに基づいて、スクリプトは14日前より古い5つのファイルのみを削除しました。14日より新しいファイルがまだ存在することを確認するために、以下のコードを実行して再度リストアップします。

Get-ChildItem c:\temp | Select-Object Name,CreationTime,@{n='AgeInDays';e={(New-TimeSpan -Start $PSItem.CreationTime).Days}}

以下の結果は、Remove-Itemが新しいファイルを削除しなかったことを確認しています。

Newer files are left untouched

PowerShellを使用してファイルのパターンを一致させて削除する

ファイルの名前、種類、拡張子に関係なく、すべてのファイルを削除することは常に最善の方法ではありません。削除プロセスで特定のファイルを明示的に除外または含める必要がある場合があります。

次の例では、*.LOGというファイル名に一致するファイルを削除する方法を示しています。一つの方法は、-Includeパラメータを使用してRemove-Itemコマンドレットを直接使用する方法です。

Remove-Item -Path C:\temp\* -Include *.log

もう一つの方法は、おそらく慎重なアプローチとして、まずGet-ChildItemを使用して削除するファイルのリストを収集することです。削除するファイルのリストに満足した場合にのみ、収集したリストをRemove-Itemコマンドレットにパイプすることができます。

たとえば、以下のコードでは、c:\temp内の*.LOGファイルを取得します。

Get-ChildItem -Path C:\temp\* -Include *.log

上記のコードの結果、Get-ChildItem*.LOGファイル名に一致するファイルのリストを返します。

Only files matching the *.log filename is returned

しかし、名前に数字5が含まれるファイルを除外したい場合はどうすればよいでしょうか?次のコードのように、-Excludeパラメータを追加することで除外することができます。

Get-ChildItem -Path C:\temp\* -Include *.log -Exclude *5*

数字5のファイルが除外されたため、結果は異なります。具体的には、以下のようにファイルFile_5.logはリストに含まれなくなります。

The file File_5.log was excluded

コードで作成されたファイルのコレクションに満足したら、Remove-Itemコマンドレットにファイルのコレクションをパイプして、最終的にそれらのファイルを削除することができます。

Get-ChildItem -Path C:\temp\* -Include *.log -Exclude *5* | Remove-Item -Verbose

最終的なコードを実行すると、選択したファイルのみを削除する目標を達成することができます。

Deleting selected files

PowerShellを使用したWMIを使用してファイルを削除する

共通のRemove-Itemコマンドレットを使用してファイルを削除する方法についての理解ができたので、より高度な使用例であるWMIの使用について説明します。

PowerShellにはWMIのサポートが付属しています。そして、WMIのサポートとは、PowerShell内からWMIクエリとメソッドを呼び出すことができることを意味します。そうです、WMIはWindowsの初期の日々に管理者が使用したVisual Basicスクリプトだけのものではありません。

MicrosoftはPowerShell 3.0でWMI固有のCIMコマンドレットをリリースしました。ファイルを削除するために使用されるCIMコマンドレットは、Get-CimInstanceInvoke-CimMethodです。

PowerShellとWMIを使用してファイルを削除する

この例では、削除する特定のファイルのパスを知っていることを前提としています。 Get-CimInstanceコマンドレットを使用して、削除するファイルの情報を取得するために、Cim_DataFileクラスを使用します。この例では、削除するファイルはC:\Temp\random.txtです。

$file2delete = Get-CimInstance -ClassName Cim_DataFile -Filter "Name = 'C:\Temp\random.txt'"
 $file2delete

上記のコードでは、-FilterパラメーターはWQL形式のクエリを受け入れます。WQLを使用する場合、バックスラッシュなど一部の文字をエスケープする必要があります。また、WQLのエスケープ文字もバックスラッシュであるため、バックスラッシュが2重化された文字\\になります。

上記のコードを実行すると、以下のデモンストレーションに示す結果が得られます。 C:\Temp\random.txtの情報が$file2delete変数に保存されます。

Getting a file using WMI query and PowerShell

今、ファイルC:\Temp\random.txtの情報が取得されたので、$file2delete変数内のオブジェクトをInvoke-CimMethodコマンドレットにパイプすることができます。 Invoke-CimMethodコマンドレットには、-Nameというパラメーターがあり、Cim_DataFileクラスのメソッド名を表します。

$file2delete | Invoke-CimMethod -Name Delete

以下のスクリーンショットで示されているように、ReturnValueが0と表示されており、これはコマンドが成功したことを意味します。

File successfully deleted using WMI and PowerShell

可能なReturnValueコードについては、このリンクを参照してください – CIM_DataFileクラスのDeleteメソッド

さらに、以下のスクリーンショットは、Invoke-CimMethod Delete()メソッドを実行した後、CIMがC:\Temp\random.txtファイルのみを削除したことを示しています。他のファイルは削除されませんでした。

C:\Temp\random.txt file was deleted

PowerShellとWMIを使用してフォルダ内のすべてのファイルを削除する方法

次の例では、PowerShellとWMIを使用してフォルダ内のすべてのファイルを削除する方法を示します。前の例で使用されたGet-CimInstanceInvoke-CimMethodのコマンドレットが使用されます。ただし、今回はWQLクエリがフォルダ内のすべてのファイルを取得します。

次のコードでは、Get-CimInstanceコマンドレットがC:\tempにあるすべてのファイルを取得します。以下のように、クエリはCim_DataFileクラスのDriveおよびPathプロパティをフィルタリングします。

$file2delete = Get-CimInstance -ClassName Cim_DataFile -Filter "Drive = 'c:' AND Path = '\\temp\\'"

上記のコードを実行すると、$file2delete変数にC:\tempのファイルに関する情報が保存されます。$file2delete変数の値を表示すると、以下の出力が表示されます。

List of all folders found in c:\temp using WMI

今、$file2delete変数に格納された値をInvoke-CimMethodにパイプして、c:\tempのすべてのファイルを削除できます。

$file2delete | Invoke-CimMethod -Name Delete

ReturnValueコードが0の場合、成功を意味します。また、deleteメソッドが呼び出された各ファイルには、個別のReturnValueコードが付いています。

All files in c:\temp deleted using WMI

PowerShellとWMIを使用して拡張子でファイルを削除する

前の例では、拡張子に関係なくフォルダ内のすべてのファイルを削除する方法を見てきました。しかし、拡張子に基づいて削除するファイルを制御することもできます。

以下のコードで確認できるように、今回のクエリには追加条件(Name LIKE '%.log)があります。この追加条件は、.LOG拡張子に一致するファイルのみが返されることを意味します。パーセント(%)記号は、WQLのLIKE演算子であり、「0個以上の文字列」を意味します。プログラミング用語では、%はワイルドカードを表すアスタリスク(*)文字と同じです。

$file2delete = Get-CimInstance -ClassName cim_datafile `
-Filter "Drive = 'c:' AND Path = '\\temp\\' AND Name LIKE '%.log'"

$file2delete | Invoke-CimMethod -Name Delete

以下のデモンストレーションは、上記のコードが実行される前に、9つの.LOGファイルと1つの.TXTファイルが存在することを示しています。コードの実行が終わると、*.LOGファイルが削除され、フォルダ内には*.TXTファイルだけが残ります。

Deleting files by extension using WMI

WMIとRemove-Itemの比較

これまでのチュートリアルでは、PowerShellを使用してファイルを削除する方法の概要を広く学びました。Remove-ItemとWMIの両方について学びました。両方の機能は似ていますが、非常に異なる方法で動作します。

ファイルを削除するためには、どちらの方法を使用すべきですか?Remove-ItemまたはWMI?

PowerShellの組み込みのコマンドレットであるGet-ChildItemRemove-Itemを使用してファイルを取得および削除すると、WMIを使用する場合よりもはるかに高速です。

以下の例では、WMIと組み込みのPowerShellコマンドレットを使用して、C:\windows\webディレクトリとそのサブディレクトリのファイルのリストを取得する場合の比較を示しています。

## Get-ChildItemを使用してC:\Windows\Web\内のすべてのファイルを再帰的にリストする
Measure-Command { Get-ChildItem C:\Windows\Web\ -Recurse}

## Get-CimInstanceおよびWMIクエリを使用してC:\Windows\Web\内のすべてのファイルを再帰的にリストする
Measure-Command { Get-CimInstance -ClassName Cim_DataFile -Filter "Drive = 'c:' AND Path = '\windows\web\%'"}

上記のコードをPowerShellで実行すると、以下のような出力が表示されます。

Get-ChildItem vs. Get-CimInstance with WMI Query

上記の出力からわかるように、C:\windows\webのファイルをリストするには、Get-CimInstanceを使用する場合は10倍ほど時間がかかりましたが、Get-ChildItemの場合は短時間で完了しました。

また、Get-ChildItemの行がGet-CimInstanceよりもずっと短いことに気づきましたか? Get-ChildItemを使用すると、実行がより高速になるだけでなく、コードもよりシンプルで短くなります。

次の手順

この記事では、組み込みのコマンドレットとWMI/CIMを使用してファイルを削除するPowerShellの2つの異なる方法を紹介しました。

常にGet-ChildItemおよびRemove-Itemコマンドレットを使用してファイルを削除することを覚えておいてください。これらの組み込みのコマンドレットは、WMIを使用する場合よりも柔軟で簡単で高速です。

ディスクスペースの整理を行うスクリプトを作成することを試みてください。もちろん、その目的で既に存在するスクリプトがあるかもしれません。それらを参考にしても構いませんが、練習して学びたいのであれば、独自のスクリプトを作成してみてください。

さらなる読み物

Source:
https://adamtheautomator.com/powershell-to-delete-files/