PowerShell でファイルやフォルダをごみ箱に入れる

2021/09/02に公開

Windows のごみ箱へ PowerShell から捨てるには次のようなコードを書きます。

$shell = New-Object -ComObject Shell.Application
$trash = $shell.NameSpace(10)
$trash.MoveHere("c:\untitled.txt")

ここで気を付けなければならないのは、ファイル名には相対パスは使えないということです。Convert-Path などを用いて絶対パスで指定しなければなりません。もっと汎用的に使うには次のようなスクリプトを使います。

[CmdletBinding(SupportsShouldProcess = $True, DefaultParameterSetName = 'Path')]
Param (
	[SupportsWildCards()]
	[Parameter(
		Mandatory = $True,
		Position = 0,
		ParameterSetName = 'Path',
		ValueFromPipeline = $True,
		ValueFromPipelineByPropertyName = $True
	)]
	[string[]]$Path,

	[Alias('LP')]
	[Alias('PSPath')]
	[Parameter(
		Mandatory = $True,
		Position = 0,
		ParameterSetName = 'LiteralPath',
		ValueFromPipeline = $False,
		ValueFromPipelineByPropertyName = $True
	)]
	[string[]]$LiteralPath
)
Begin {
	$shell = New-Object -ComObject Shell.Application
	$trash = $shell.NameSpace(10)
}
Process {
	if ($PSBoundParameters.ContainsKey('Path')) {
		$targets = Convert-Path $Path
	} else {
		$targets = Convert-Path -LiteralPath $LiteralPath
	}
	$targets | Foreach-Object {
		if ($PSCmdlet.ShouldProcess($_)) {
			$trash.MoveHere($_)
		}
	}
}

このコードを解説します。

[CmdletBinding(SupportsShouldProcess = $True, DefaultParameterSetName = 'Path')]

SupportsShouldProcess = $True にすると、-WhatIf -Confirm などの引数が使えるようになります。

例えば先ほどのスクリプトを Remove-ToRecycleBin.ps1 という名前で保存した場合、Remove-ToRecycleBin * -Confirm のように実行すると、次のように該当するファイルの一つ一つに対して本当に削除していいのかどうか確認プロンプトが表示されます。

PS> Remove-ToRecycleBin .\a.txt -Confirm

Confirm
Are you sure you want to perform this action?
Performing the operation "Remove-ToRecycleBin.ps1" on target "C:\Users\Zuishin\Desktop\a.txt".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): n

Confirm
Are you sure you want to perform this action?
Performing the operation "Remove-ToRecycleBin.ps1" on target "C:\Users\Zuishin\Desktop\b.txt".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

また、DefaultParameterSetName = 'Path' はデフォルトのパラメータセットを Path に指定しています。PowerShell はパラメータセットを用いて引数の組み合わせをオーバーロードすることができます。

参考: Use Parameter Sets to Simplify PowerShell Commands

Param (
	[SupportsWildCards()]
	[Parameter(
		Mandatory = $True,
		Position = 0,
		ParameterSetName = 'Path',
		ValueFromPipeline = $True,
		ValueFromPipelineByPropertyName = $True
	)]
	[string[]]$Path,

	[Alias('LP')]
	[Alias('PSPath')]
	[Parameter(
		Mandatory = $True,
		Position = 0,
		ParameterSetName = 'LiteralPath',
		ValueFromPipeline = $False,
		ValueFromPipelineByPropertyName = $True
	)]
	[string[]]$LiteralPath
)

ここでは PathLiteralPath の二つの引数を定義しています。Path はワイルドカードを受け付けることを示すために SupportsWildCards 属性を付けます。自動的に展開されるわけではないので、実際の展開は自分で書く必要があります。

Mandatory = $True はこの引数が必須であることを示します。必須の引数が指定されなかった場合、それを入力するためのプロンプトが表示されます。

Position = 0 はこの引数が最初の引数であることを示します。Remove-ToRecycleBin a.txt のように実行した場合、-Path をつけなくてもデフォルトのパラメータセットである Path パラメータセットの最初の引数である $Path の指定と推論されます。

パイプラインから引数を受け取るには二種類の受け取り方があります。PathValueFromPipeline = $True になっているので、入力されたデータがそのまま $Path に代入されます。

一方、LiteralPathValueFromPipeline = $False になっているので入力されたデータをそのまま受け取ることはできませんが、ValueFromPipelineByPropertyName = $True になっています。このため、入力されたデータに LiteralPath というプロパティがあった場合、そのプロパティの内容が $LiteralPath に代入されます。

また、LiteralPathLP PSPath の二つのエイリアスを持っています。このため、-LiteralPath a.txt の代わりに -LP a.txt と書くことができます。そして、Get-ChildItem などの出力は PSPath プロパティを持っていて、そこにファイルのフルパスが入っています。そのため、Get-ChildItem | Remove-ToRecycleBin のようにパイプを使って入力することが可能です。

Begin {
	$shell = New-Object -ComObject Shell.Application
	$trash = $shell.NameSpace(10)
}

Begin ブロックで COM オブジェクトである Shell.Application と、ごみ箱を表す $trash を作っています。

if ($PSBoundParameters.ContainsKey('Path')) {
	$targets = Convert-Path $Path
} else {
	$targets = Convert-Path -LiteralPath $LiteralPath
}

引数 Path が指定されている時は、$targets にはワイルドカードを展開してフルパスに直したものを入れます。また LiteralPath が指定されている時は、$targets にはワイルドカードを展開せずフルパスに直したものを入れます。

$targets | Foreach-Object {
	if ($PSCmdlet.ShouldProcess($_)) {
		$trash.MoveHere($_)
	}
}

$targets の各要素に対して $trash.MoveHere を適用していますが、$PSCmdlet.ShouldProcess 判定をすることにより、削除を実行すべきでないファイルを残すようにしています。

執筆日: 2019/03/02

GitHubで編集を提案

Discussion