开发者

PowerShell remove-item succeeds but throws exception

开发者 https://www.devze.com 2023-03-22 03:00 出处:网络
I\'m running the following command in a directory that is the root of a Mercurial repository. I want to delete any files and folders beginning with \".hg\":

I'm running the following command in a directory that is the root of a Mercurial repository. I want to delete any files and folders beginning with ".hg":

gci -rec -filter ".hg*" | remove-item -recurse -force

The strange thing, is that it does actually work, but still produces the following exception:

Get-ChildItem : Could not find a part of the path 'C:\temp\WSCopyTest\MyCompany.Services.DaftPunk\.hg\'.
At line:1 char:4
+ gci <<<<  -rec -filter ".hg*" | remove-item -recurse -force
 开发者_开发百科   + CategoryInfo          : ReadError: (C:\temp\WSCopyT...es.DaftPunk\.hg:String) [Get-ChildItem], DirectoryNotFoundException
    + FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand

Because the exception is thrown by Get-ChildItem, I suspect my understanding of the pipelining in PowerShell is flawed. Almost like Get-ChildItem finds an item, passes it to the next cmdlet in the pipeline, then looks for the next one? Is that how it works?

The following was supposed to be a script that would replicate the problem, but, on the same machine, it works flawlessly:

$repoRoot = "C:\Temp\HgDeleteTest\MyRepo"
remove-item $repoRoot -recurse -force
md $repoRoot
pushd $repoRoot
hg.exe init
add-content ShouldBeLeftBehind.txt "This is some text in a file that should be left behind once the various .hg files/directories are removed."
add-content .hgignore syntax:glob
add-content .hgignore *.dll
add-content .hgignore *.exe
hg.exe commit --addremove --message "Building test repository with one commit."
gci -rec -filter ".hg*" | remove-item -recurse -force
popd


Could it be that you're removing a folder starting with .hg, which in turn contains a file starting with .hg, but that file no longer exists?


I expect Antony is correct - using the -recurse on the gci will enumerate both files and directories that match ".hg*". The directory will be returned first. Then remove-item deletes the directory first, and the -force switch deletes all the files in it. Then it tries to delete the files that were in that directory that match ".hg*" (that were there when the gci ran) but they're gone now. This should stop the errors:

gci ".hg*" -recurse | select -expand fullname | sort length -desc | remove-item -force

Sorting the full names in descending order of length insures that no matched parent directory is deleted before all the matched files in that directory are deleted.


The following will restrict its deletions to files only, and exclude folders.

gci -rec -filter ".hg*" | Where {$_.psIsContainer -eq $false} | remove-item -recurse -force

Then run it again, by deleting the folders:

gci -rec -filter ".hg*" | Where {$_.psIsContainer -eq $true} | remove-item -recurse -force


I ended up separating the search from the removal. I believe my issue was to do with the way piping works by default (i.e. one item at a time) but I've not been able to build a test to check it.

In any case, the following works fine:

$hgObjects = gci -rec | ?{ $_.name -like ".hg*" -and $_.name -ne ".hgignore" }
remove-item $hgObjects -force -recurse

Using this style, the remove-item cmdlet gets an array of items found and works through them.

The .hg folder in my original example didn't have anything in it called .hg*, so I don't see what was going on. It felt more like it was trying to delete the folder twice, which I find very strange. I wonder if it's actually a PowerShell manifestation of this standard .NET behaviour:

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying or deleting elements, the enumerator can be invalidated and the next call to MoveNext or Reset can throw an InvalidOperationException. If the collection is modified between MoveNext and Current, Current returns the element that it is set to, even if the enumerator is already invalidated. (Taken from http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.getenumerator(v=vs.71).aspx).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号