LoginSignup
11
2

More than 3 years have passed since last update.

削除後同名のフォルダを作成するとエラーになる

Last updated at Posted at 2019-11-29

富士通システムズウェブテクノロジー Advent Calendar 2019 2日目の投稿です
記事の内容は全て個人の見解であり、執筆内容は執筆者自身の責任です。所属する組織は関係ありません。

Windows機内の一時ファイル置き場に沢山の不要ファイルが溜まってきたので
単純に削除するのではなく、せっかくだから今まで使ってこなかったPowerShellを使ってみようとしました。
その際、はまってしまったことを整理した記事です。

発生したトラブル :dizzy_face:

フォルダの作成に失敗します。

やりたいこと :sunny:

不要なファイルが入っているフォルダを削除し、新しい同名のフォルダを作成します。

実行環境 :computer:

Windows10 Enterprise バージョン1809

実行したスクリプト :cd:

Trash.ps1
$target = "C:\temp\Trash"

# 不要フォルダを削除する
if (Test-Path $target) {
    Remove-Item $target -Recurse
}
# 新規にフォルダを作成する
New-Item $target -itemType Directory | Out-Null

エラーの詳細 :worried:

以下の2つのエラーのどちらかが発生します。
後者はおそらく削除のためにロックをかけているのでしょう。
そのタイミングで作成しようとすると発生すると思われます。

C:\Scripts\  > .\Trash.ps1
New-Item : 指定された名前 C:\temp\Trash の項目は既に存在します。
発生場所 C:\Scripts\Trash.ps1:8 文字:1
+ New-Item $target -itemType Directory | Out-Null
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\temp\Trash:String) [New-Item], IOException
    + FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand

C:\Scripts\  >
C:\Scripts\  > .\Trash.ps1
New-Item : アクセスが拒否されました。
発生場所 C:\Scripts\Trash.ps1:8 文字:1
+ New-Item $target -itemType Directory | Out-Null
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\temp\Trash:String) [New-Item], UnauthorizedAccessException
    + FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.NewItemCommand

C:\Scripts\  >

原因 :scream_cat:

PowerShellのRemove-Itemコマンドは、別プロセスで実行されるらしく、
削除完了を待たずに、次の処理に移行してしまいます。
すると、C:\temp\Trashの削除が完了する前にC:\temp\Trashを作成しようとするため
今回のトラブルが発生しました。

対応方法 :bulb:

対応方法は大きく3つ思いつきました。
1. 削除完了を待ってから作成を行う
2. 削除対象を別名で退避し、作成を行う
3. 外部アプリを使う。
別途削除アプリを作り、PowerShellから待ち受けで実行すればできますが、
それは今回の目的から外れるので1と2を試しました。

「削除完了を待ってから作成を行う」場合 :point_up:

Remove-Itemコマンドを実行後、Start-Sleepコマンドを使い何秒か経過してから
New-Itemコマンドを実行しても良いのですが、適切なスリープ時間を決めることは難しいです。
そこで、C:\temp\Trashが存在しているかどうかをチェックし、
存在しなくなったら作成するようにしたのが、次のスクリプトです。

Trash.ps1
$target = "C:\temp\Trash"

# 不要フォルダを削除する
if (Test-Path $target) {
    Remove-Item $targetBk -Recurse
}

# 対象の存在チェックを繰り返す
while (Test-Path $target) {
    # まだ存在しているので1秒待ってから再度チェックを行う
    Start-Sleep -Seconds 1
}

# 新規にフォルダを作成する
New-Item $target -itemType Directory | Out-Null

削除される対象の量の大小によって、Sleep時間は増減させればいいでしょう。
ただし、このスクリプトにはトラブルが発生しました。

whileのTest-Pathの際に、アクセスエラーが発生することがあるのです。
原因はわかりません。

「削除対象を別名で退避し、作成を行う」場合 :v:

Rename-ItemもしくはMove-Itemコマンドで対象をリネームすることで
削除処理の影響をなくしたのが、次のスクリプトです。

Trash.ps1
$target = "C:\temp\Trash"

# 不要フォルダを削除する
if (Test-Path $target) {
    $targetBk = $target + "Bk"
    # 一度退避してから削除する
    Rename-Item $target $targetBk
    Remove-Item $targetBk -Recurse
}

# 新規にフォルダを作成する
New-Item $target -itemType Directory | Out-Null

フォルダを作成と削除を分けることでトラブルを回避します。
先の「削除完了を待ってから作成を行う」と異なり、こちらは安定して動作しました。

まとめ :smiley:

もともと削除処理は結果を待ち受けません。
それを無理に待ち受ける処理にするよりも
その動作をそのまま受け入れた方式の方が自然に動作するようです。

11
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
2