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 Cmdletを使用する

ファイルを単に削除する必要がある場合、おそらくすぐに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 コマンドレットは -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 コマンドレットに-Recurse スイッチを追加してファイルを再帰的に取得する必要があります。

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

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

Failed to retrieve files in sub-folders

長いパスの問題を回避する

上記のエラーは、PowerShellが“パスの一部が見つかりませんでした”と表示しています。 このエラーは、コマンドレットがアクセスしようとしているパスが存在しないことを示しています – これは誤解を招くものです。

この場合、エラーの原因は、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 cmdletから-Fileパラメータを削除するだけで、PowerShellがファイルとフォルダを含むすべてのアイテムを削除するはずです。

x日より古いファイルを削除するためにPowerShellを使用する

ディスク容量の整理の典型的な例のもう一つは、特定の日数よりも古いファイルを削除することです。この例は、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ファイル名に一致するファイルを削除する方法を示しています。これを行う方法の1つは、-Includeパラメーターを使用してRemove-Itemコマンドレットを直接使用する方法です。

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

もう1つの方法は、おそらく、慎重なアプローチであると言えるでしょう。まず、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を使用してファイルを削除する

これで、ファイルを削除するための一般的なRemove-Itemコマンドレットの使用方法がわかったので、より高度な使用例であるWMIの使用に移りましょう。

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

MicrosoftはPowerShell 3.0でWMI固有のCIMコマンドレットをリリースしました。ファイルを削除するために使用されるCIMコマンドレットは、Get-CimInstanceおよびInvoke-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 のエスケープ文字もバックスラッシュであるため、二重バックスラッシュ文字 \\ が生成されます。

上記のコードを実行すると、以下のデモで示されている結果が生成されます。 C:\Temp\random.txt の情報は $file2delete 変数に保存されます。

Getting a file using WMI query and PowerShell

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

$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クエリが1つの特定のファイルではなく、フォルダー内のすべてのファイルを取得します。

次のコードでは、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変数に格納されている値をc:\tempのすべてのファイルを削除するためにInvoke-CimMethodにパイプで渡すことができます。

$file2delete | Invoke-CimMethod -Name Delete

ReturnValueコードが0の場合、成功を意味し、削除メソッドが呼び出された各ファイルには独自のReturnValueコードがあります。

All files in c:\temp deleted using WMI

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

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

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

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

$file2delete | Invoke-CimMethod -Name Delete

以下のデモンストレーションでは、上記のコードが実行される前に、9つの *.LOG ファイルと *.TXT ファイルが1つだけあることがわかります。コードの実行が完了すると、*.LOG ファイルが削除され、*.TXT ファイルだけがフォルダーに残ります。

Deleting files by extension using WMI

WMIとRemove-Itemの比較

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

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

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

以下の例では、WMIと組み込みのPowerShell cmdletを使用して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

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

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

次のステップ

この記事では、組み込みのcmdletおよびWMI/CIMを使用してファイルを削除するPowerShellの2つの異なる方法を見てきました。

ファイルを削除する場合は常にGet-ChildItemおよびRemove-Item cmdletを使用する必要があることを知っておいてください。これらの組み込みのcmdletは、WMIを使用する場合よりも柔軟で簡単で高速です。

ディスクスペースの整理を自動化するスクリプトを作成してみてください。既存のスクリプトを参考にしても構いませんが、練習して学びたいのであれば、自分で作成してみてください。

さらなる参考資料

Source:
https://adamtheautomator.com/powershell-delete-file/