Kir*_*ari 2 windows powershell
使用Remove-Item命令时,即使使用-r和-Force参数,有时也会返回以下错误消息:
Remove-Item:无法删除项目C:\ Test Folder \ Test Folder \ Target:目录不为空。
特别是在Windows资源管理器中打开要删除的目录时,会发生这种情况。
现在,虽然可以通过关闭Windows资源管理器或不浏览该位置来避免这种情况,但是我在多用户环境中工作脚本(在这种环境下,人们有时会忘记关闭Windows资源管理器窗口,但我对删除的解决方案感兴趣)即使在Windows资源管理器中打开了整个文件夹和目录。
有没有比-Force我可以设置的功能更强大的选项了?
为了可靠地重现此内容,请创建文件夹C:\Test Folder\Origin并在其中填充一些文件和子文件夹(重要),然后采用以下脚本或类似的脚本并执行一次。现在打开C:\Test Folder\Target(在我的情况下,我使用C:\Test Folder\Target\Another Subfoldercontains A third file.txt)的子文件夹之一,然后尝试再次运行该脚本。您现在将得到错误。如果您第三次运行该脚本,则不会再次收到该错误(但是,根据我尚未确定的情况,该错误有时会第二次发生,然后再也不会发生,有时又每第二次发生一次)。
$SourcePath = "C:\Test Folder\Origin"
$TargetPath = "C:\Test Folder\Target"
if (Test-Path $TargetPath) {
Remove-Item -r $TargetPath -Force
}
New-Item -ItemType directory -Path $TargetPath
Copy-Item $SourcePath -Destination $TargetPath -Force -Recurse -Container
Run Code Online (Sandbox Code Playgroud)
mkl*_*nt0 11
更新:似乎有计划使Windows文件系统项删除API同步,但是从Windows 10版本1903开始它们尚未同步-请参阅GitHub上的注释。
最终这仅是一个时间问题:尝试删除父目录时,子目录的最后一个句柄可能尚未关闭-这是一个基本问题,不限于打开文件资源管理器窗口:
不可思议的是,Windows文件和目录删除API是异步的:也就是说,到函数调用返回时,不能保证删除已经完成。
遗憾的是,Remove-Item无法说清是 -既不做cmd.exe的rd /s和.NET的[System.IO.Directory]::Delete()-看到这个答案的详细信息。这会导致间歇性的,不可预测的故障。
该解决办法进来的礼貌此YouTube视频(在7:35开始),PowerShell的执行当中低于:
同步目录删除功能Remove-FileSystemItem:
重要:
同步自定义实现仅在Windows上才需要,因为类似Unix的平台上的文件删除系统调用首先是同步的。因此,该功能仅适用Remove-Item于类Unix平台。在Windows上,自定义实现:
什么不会阻止可靠的移除:
至少在Windows 10上,文件资源管理器不会锁定显示的目录,因此不会阻止删除。
PowerShell也不会锁定目录,因此拥有当前位置为目标目录或其子目录之一的另一个PowerShell窗口也不会阻止删除(相反,cmd.exe 它会锁定-参见下文)。
在目标目录的子树中用FILE_SHARE_DELETE/ [System.IO.FileShare]::Delete(很少见)打开的文件也不会阻止删除,尽管它们确实以父目录中的临时名称存在,直到关闭它们的最后一个句柄为止。
什么会阻止移除:
如果存在权限问题(如果ACL阻止删除),则删除将中止。
如果遇到无限期锁定的文件或目录,则删除将中止。值得注意的是,这包括:
cmd.exe(命令提示符)与PowerShell不同,它确实锁定了作为其当前目录的目录,因此,如果cmd.exe打开的窗口的当前目录是目标目录或其子目录之一,则删除将失败。
如果应用程序在目标目录的子树中使文件保持打开状态,而该文件未通过文件共享模式FILE_SHARE_DELETE/ 打开[System.IO.FileShare]::Delete(很少使用此模式),则删除将失败。请注意,这仅适用于在使用文件内容时保持文件打开状态的应用程序。(例如Microsoft Office应用程序),而文本编辑器(例如记事本和Visual Studio Code)则不会保持打开状态。
隐藏的文件和具有只读属性的文件:
Remove-Item -Force。Windows上可靠的自定义实现以降低性能为代价。
function Remove-FileSystemItem {
<#
.SYNOPSIS
Removes files or directories reliably and synchronously.
.DESCRIPTION
Removes files and directories, ensuring reliable and synchronous
behavior across all supported platforms.
The syntax is a subset of what Remove-Item supports; notably,
-Include / -Exclude and -Force are NOT supported; -Force is implied.
As with Remove-Item, passing -Recurse is required to avoid a prompt when
deleting a non-empty directory.
IMPORTANT:
* On Unix platforms, this function is merely a wrapper for Remove-Item,
where the latter works reliably and synchronously, but on Windows a
custom implementation must be used to ensure reliable and synchronous
behavior. See https://github.com/PowerShell/PowerShell/issues/8211
* On Windows:
* The *parent directory* of a directory being removed must be
*writable* for the synchronous custom implementation to work.
* The custom implementation is also applied when deleting
directories on *network drives*.
* If an indefinitely *locked* file or directory is encountered, removal is aborted.
By contrast, files opened with FILE_SHARE_DELETE /
[System.IO.FileShare]::Delete on Windows do NOT prevent removal,
though they do live on under a temporary name in the parent directory
until the last handle to them is closed.
* Hidden files and files with the read-only attribute:
* These are *quietly removed*; in other words: this function invariably
behaves like `Remove-Item -Force`.
* Note, however, that in order to target hidden files / directories
as *input*, you must specify them as a *literal* path, because they
won't be found via a wildcard expression.
* The reliable custom implementation on Windows comes at the cost of
decreased performance.
.EXAMPLE
Remove-FileSystemItem C:\tmp -Recurse
Synchronously removes directory C:\tmp and all its content.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium', DefaultParameterSetName='Path', PositionalBinding=$false)]
param(
[Parameter(ParameterSetName='Path', Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string[]] $Path
,
[Parameter(ParameterSetName='Literalpath', ValueFromPipelineByPropertyName)]
[Alias('PSPath')]
[string[]] $LiteralPath
,
[switch] $Recurse
)
begin {
# !! Workaround for https://github.com/PowerShell/PowerShell/issues/1759
if ($ErrorActionPreference -eq [System.Management.Automation.ActionPreference]::Ignore) { $ErrorActionPreference = 'Ignore'}
$targetPath = ''
$yesToAll = $noToAll = $false
function trimTrailingPathSep([string] $itemPath) {
if ($itemPath[-1] -in '\', '/') {
# Trim the trailing separator, unless the path is a root path such as '/' or 'c:\'
if ($itemPath.Length -gt 1 -and $itemPath -notmatch '^[^:\\/]+:.$') {
$itemPath = $itemPath.Substring(0, $itemPath.Length - 1)
}
}
$itemPath
}
function getTempPathOnSameVolume([string] $itemPath, [string] $tempDir) {
if (-not $tempDir) { $tempDir = [IO.Path]::GetDirectoryName($itemPath) }
[IO.Path]::Combine($tempDir, [IO.Path]::GetRandomFileName())
}
function syncRemoveFile([string] $filePath, [string] $tempDir) {
# Clear the ReadOnly attribute, if present.
if (($attribs = [IO.File]::GetAttributes($filePath)) -band [System.IO.FileAttributes]::ReadOnly) {
[IO.File]::SetAttributes($filePath, $attribs -band -bnot [System.IO.FileAttributes]::ReadOnly)
}
$tempPath = getTempPathOnSameVolume $filePath $tempDir
[IO.File]::Move($filePath, $tempPath)
[IO.File]::Delete($tempPath)
}
function syncRemoveDir([string] $dirPath, [switch] $recursing) {
if (-not $recursing) { $dirPathParent = [IO.Path]::GetDirectoryName($dirPath) }
# Clear the ReadOnly attribute, if present.
# Note: [IO.File]::*Attributes() is also used for *directories*; [IO.Directory] doesn't have attribute-related methods.
if (($attribs = [IO.File]::GetAttributes($dirPath)) -band [System.IO.FileAttributes]::ReadOnly) {
[IO.File]::SetAttributes($dirPath, $attribs -band -bnot [System.IO.FileAttributes]::ReadOnly)
}
# Remove all children synchronously.
$isFirstChild = $true
foreach ($item in [IO.directory]::EnumerateFileSystemEntries($dirPath)) {
if (-not $recursing -and -not $Recurse -and $isFirstChild) { # If -Recurse wasn't specified, prompt for nonempty dirs.
$isFirstChild = $false
# Note: If -Confirm was also passed, this prompt is displayed *in addition*, after the standard $PSCmdlet.ShouldProcess() prompt.
# While Remove-Item also prompts twice in this scenario, it shows the has-children prompt *first*.
if (-not $PSCmdlet.ShouldContinue("The item at '$dirPath' has children and the -Recurse switch was not specified. If you continue, all children will be removed with the item. Are you sure you want to continue?", 'Confirm', ([ref] $yesToAll), ([ref] $noToAll))) { return }
}
$itemPath = [IO.Path]::Combine($dirPath, $item)
([ref] $targetPath).Value = $itemPath
if ([IO.Directory]::Exists($itemPath)) {
syncremoveDir $itemPath -recursing
} else {
syncremoveFile $itemPath $dirPathParent
}
}
# Finally, remove the directory itself synchronously.
([ref] $targetPath).Value = $dirPath
$tempPath = getTempPathOnSameVolume $dirPath $dirPathParent
[IO.Directory]::Move($dirPath, $tempPath)
[IO.Directory]::Delete($tempPath)
}
}
process {
$isLiteral = $PSCmdlet.ParameterSetName -eq 'LiteralPath'
if ($env:OS -ne 'Windows_NT') { # Unix: simply pass through to Remove-Item, which on Unix works reliably and synchronously
Remove-Item @PSBoundParameters
} else { # Windows: use synchronous custom implementation
foreach ($rawPath in ($Path, $LiteralPath)[$isLiteral]) {
# Resolve the paths to full, filesystem-native paths.
try {
# !! Convert-Path does find hidden items via *literal* paths, but not via *wildcards* - and it has no -Force switch (yet)
# !! See https://github.com/PowerShell/PowerShell/issues/6501
$resolvedPaths = if ($isLiteral) { Convert-Path -ErrorAction Stop -LiteralPath $rawPath } else { Convert-Path -ErrorAction Stop -path $rawPath}
} catch {
Write-Error $_ # relay error, but in the name of this function
continue
}
try {
$isDir = $false
foreach ($resolvedPath in $resolvedPaths) {
# -WhatIf and -Confirm support.
if (-not $PSCmdlet.ShouldProcess($resolvedPath)) { continue }
if ($isDir = [IO.Directory]::Exists($resolvedPath)) { # dir.
# !! A trailing '\' or '/' causes directory removal to fail ("in use"), so we trim it first.
syncRemoveDir (trimTrailingPathSep $resolvedPath)
} elseif ([IO.File]::Exists($resolvedPath)) { # file
syncRemoveFile $resolvedPath
} else {
Throw "Not a file-system path or no longer extant: $resolvedPath"
}
}
} catch {
if ($isDir) {
$exc = $_.Exception
if ($exc.InnerException) { $exc = $exc.InnerException }
if ($targetPath -eq $resolvedPath) {
Write-Error "Removal of directory '$resolvedPath' failed: $exc"
} else {
Write-Error "Removal of directory '$resolvedPath' failed, because its content could not be (fully) removed: $targetPath`: $exc"
}
} else {
Write-Error $_ # relay error, but in the name of this function
}
continue
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1635 次 |
| 最近记录: |