忽略不存在文件的PowerShell的rm-f等效项 背景

忽略不存在文件的PowerShell的rm-f等效项 背景,powershell,rm,Powershell,Rm,我有一个PowerShell脚本,可以将一些结果写入文件 我想在脚本开始时使用remove Item自动删除结果文件 您可以手动删除结果文件,因此即使结果文件不存在,我也不想显示错误消息 我想在脚本由于其他原因(例如文件被锁定)无法删除结果文件时显示错误消息 在类Unix系统中,使用rm-f可以满足上述所有要求 问题 首先,我尝试了Remove Item-Force,但它无法忽略不存在的文件(参见rm-f忽略不存在的文件) 接下来,我尝试了Remove Item-ErrorAction Ig

我有一个PowerShell脚本,可以将一些结果写入文件

  • 我想在脚本开始时使用
    remove Item
    自动删除结果文件
  • 您可以手动删除结果文件,因此即使结果文件不存在,我也不想显示错误消息
  • 我想在脚本由于其他原因(例如文件被锁定)无法删除结果文件时显示错误消息
在类Unix系统中,使用
rm-f
可以满足上述所有要求

问题 首先,我尝试了
Remove Item-Force
,但它无法忽略不存在的文件(参见
rm-f
忽略不存在的文件)

接下来,我尝试了
Remove Item-ErrorAction Ignore
Remove Item-ErrorAction SilentlyContinue
,但在删除文件失败时,它们不会显示错误消息(参见
rm-f
显示类似
rm:cannot Remove'foo.txt:在这种情况下不允许操作)

问题:
PowerShell中是否有满足上述所有要求的rm-f
等效产品?

对我来说,最简单的解决方案是:

if (test-path $file) {
  remove-item $file
}
我也想到了这一点$错误[0]始终是最近的错误

remove-item $file -erroraction silentlycontinue
if ($error[0] -notmatch 'does not exist') {
  write-error $error[0]  # to standard error
}
我认为您也可以使用try/catch来处理特定的异常。这里有一个例子。我通过制表符完成找到了异常。但脚本将停止与其他未捕获的异常。此错误通常不会停止

try { remove-item foo -erroraction stop }
catch [System.Management.Automation.ItemNotFoundException] { $null }
'hi'

您不能单独使用该cmdlet执行此操作。您必须为错误提供额外的逻辑

'D:\temp\abc.txt', 'D:\Temp\hw.txt', 'D:\Temp\nonexistent.txt.', 'D:\Temp\book1.csv' | 
ForEach{
    try   {Remove-Item   -Path    $PSitem -WhatIf -ErrorAction Stop}
    catch {Write-Warning -Message $PSItem.Exception.Message}
}
# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
What if: Performing the operation "Remove File" on target "D:\Temp\hw.txt".
WARNING: Cannot find path 'D:\Temp\nonexistent.txt.' because it does not exist.
What if: Performing the operation "Remove File" on target "D:\Temp\book1.csv".
#>
'D:\temp\abc.txt'、'D:\temp\hw.txt'、'D:\temp\nonexistent.txt'、'D:\temp\book1.csv'
弗雷奇{
尝试{Remove Item-Path$PSitem-WhatIf-ErrorAction Stop}
捕获{写入警告-消息$PSItem.Exception.Message}
}
#结果
您应该在所有代码(交互式和脚本)中利用错误处理。 您可以将任何屏幕输出发送到$null、Out null或[void]以防止它进入屏幕,但仍然知道它发生了

对于您要求的用例,您将需要多个逻辑(try/catch、if/then)语句

因此,类似于修改后的包装器函数:

function Remove-ItemNotFileLocked
{
    [cmdletbinding(SupportsShouldProcess)]
    [Alias('rinf')]

    param 
    (
        [parameter(Mandatory = $true)][string]$FullFilePath
    )

    $TargetFile = New-Object System.IO.FileInfo $FullFilePath

    if ((Test-Path -Path $FullFilePath) -eq $false) {return $false}

    try 
    {
        $TargetFileStream = $TargetFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)

        if ($TargetFileStream) 
        {
            $TargetFileStream.Close()
            Remove-Item -Path $FullFilePath
        }
        $false
    } 
    catch 
    {
        $true
    }
}

'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach {Remove-ItemNotFileLocked -FullFilePath $PSItem -WhatIf}

# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
True
False
#>
函数删除ItemNotFileLocked
{
[cmdletbinding(SupportsShouldProcess)]
[别名('rinf')]
param
(
[参数(必需=$true)][字符串]$FullFilePath
)
$TargetFile=新对象System.IO.FileInfo$FullFilePath
if((测试路径-Path$FullFilePath)-eq$false){return$false}
尝试
{
$TargetFileStream=$TargetFile.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::读写,[System.IO.FileShare]::无)
如果($TargetFileStream)
{
$TargetFileStream.Close()
删除项-路径$FullFilePath
}
$false
} 
抓住
{
$true
}
}
“D:\temp\abc.txt”、“D:\Documents\Return To Sender.docx”、“D:\temp\nonexistent.txt.”
ForEach{Remove ItemNotFileLocked-FullFilePath$PSItem-WhatIf}
#结果
注意事项:记事本等文本编辑器不会锁定文件。

  • 第一条消息显示一个记事本文件已打开,但可以删除
  • 第二个显示Word文档已打开并锁定
  • 第三个显示的是文本文件,而不是系统上的文本文件
如果我不想要屏幕上的噪音,那么。。。 注释掉那些$false和$True语句,它们用于调试和验证工作

'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach {$null = Remove-ItemNotFileLocked -FullFilePath $PSItem -WhatIf}
# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
#>
'D:\temp\abc.txt','D:\Documents\Return To Sender.docx','D:\temp\nonexistent.txt'
ForEach{$null=Remove ItemNotFileLocked-FullFilePath$PSItem-WhatIf}
#结果
当然,删除/注释掉-WhatIf以允许事情发生,并且噪音也会消失

如果您不想使用函数,那么这个代码块应该可以解决您的用例

# Remove non-Locked file and show screen output
'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach{
    try   
    {
        $TargetFile = (New-Object System.IO.FileInfo $PSitem).Open(
                                            [System.IO.FileMode]::Open, 
                                            [System.IO.FileAccess]::ReadWrite, 
                                            [System.IO.FileShare]::None
                      )
        $TargetFile.Close()  
        Remove-Item -Path $PSItem -WhatIf  
    }
    catch [System.Management.Automation.ItemNotFoundException]{$PSItem.Exception.Message}
    catch {$PSItem.Exception.Message}
}

# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
Exception calling "Open" with "3" argument(s): "The process cannot access the file 'D:\Documents\Return To Sender.docx' because it is being used by another process."
Exception calling "Open" with "3" argument(s): "Could not find file 'D:\Temp\nonexistent.txt'."
#>

# Remove non-Locked file and silence screen output
'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach{
    try   
    {
        $TargetFile = (New-Object System.IO.FileInfo $PSitem).Open(
                                            [System.IO.FileMode]::Open, 
                                            [System.IO.FileAccess]::ReadWrite, 
                                            [System.IO.FileShare]::None
                      )
        $TargetFile.Close()  
        Remove-Item -Path $PSItem -WhatIf 
    }
    catch [System.Management.Automation.ItemNotFoundException]{$null = $PSItem.Exception.Message}
    catch {$null = $PSItem.Exception.Message}
}
# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
#>
#删除未锁定的文件并显示屏幕输出
“D:\temp\abc.txt”、“D:\Documents\Return To Sender.docx”、“D:\temp\nonexistent.txt.”
弗雷奇{
尝试
{
$TargetFile=(新对象System.IO.FileInfo$PSitem)。打开(
[System.IO.FileMode]::打开,
[System.IO.FileAccess]::读写,
[System.IO.FileShare]::无
)
$TargetFile.Close()
删除项目-路径$PSItem-WhatIf
}
catch[System.Management.Automation.ItemNotFoundException]{$PSItem.Exception.Message}
捕获{$PSItem.Exception.Message}
}
#结果
#删除未锁定的文件和静音屏幕输出
“D:\temp\abc.txt”、“D:\Documents\Return To Sender.docx”、“D:\temp\nonexistent.txt.”
弗雷奇{
尝试
{
$TargetFile=(新对象System.IO.FileInfo$PSitem)。打开(
[System.IO.FileMode]::打开,
[System.IO.FileAccess]::读写,
[System.IO.FileShare]::无
)
$TargetFile.Close()
删除项目-路径$PSItem-WhatIf
}
catch[System.Management.Automation.ItemNotFoundException]{$null=$PSItem.Exception.Message}
catch{$null=$PSItem.Exception.Message}
}
#结果

感谢您的评论。第一个似乎有TOCTOU问题,第二个似乎不适用于非英语环境。但它简单易懂,因此对于在单一(英语)环境中使用PowerShell的用户很有帮助。第三种方法最适合我的环境(我在日语环境中使用Windows10),并且非常简单,每个人都能理解。我认为这个答案非常有用。谢谢你的评论。它似乎有一个文件锁定的问题,但这个问题不会在我的上下文中实现。不幸的
function Remove-ItemNotFileLocked
{
    [cmdletbinding(SupportsShouldProcess)]
    [Alias('rinf')]

    param 
    (
        [parameter(Mandatory = $true)][string]$FullFilePath
    )

    $TargetFile = New-Object System.IO.FileInfo $FullFilePath

    if ((Test-Path -Path $FullFilePath) -eq $false) {return $false}

    try 
    {
        $TargetFileStream = $TargetFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)

        if ($TargetFileStream) 
        {
            $TargetFileStream.Close()
            Remove-Item -Path $FullFilePath
        }
        $false
    } 
    catch 
    {
        $true
    }
}

'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach {Remove-ItemNotFileLocked -FullFilePath $PSItem -WhatIf}

# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
True
False
#>
'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach {$null = Remove-ItemNotFileLocked -FullFilePath $PSItem -WhatIf}
# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
#>
# Remove non-Locked file and show screen output
'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach{
    try   
    {
        $TargetFile = (New-Object System.IO.FileInfo $PSitem).Open(
                                            [System.IO.FileMode]::Open, 
                                            [System.IO.FileAccess]::ReadWrite, 
                                            [System.IO.FileShare]::None
                      )
        $TargetFile.Close()  
        Remove-Item -Path $PSItem -WhatIf  
    }
    catch [System.Management.Automation.ItemNotFoundException]{$PSItem.Exception.Message}
    catch {$PSItem.Exception.Message}
}

# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
Exception calling "Open" with "3" argument(s): "The process cannot access the file 'D:\Documents\Return To Sender.docx' because it is being used by another process."
Exception calling "Open" with "3" argument(s): "Could not find file 'D:\Temp\nonexistent.txt'."
#>

# Remove non-Locked file and silence screen output
'D:\temp\abc.txt', 'D:\Documents\Return To Sender.docx','D:\Temp\nonexistent.txt.' | 
ForEach{
    try   
    {
        $TargetFile = (New-Object System.IO.FileInfo $PSitem).Open(
                                            [System.IO.FileMode]::Open, 
                                            [System.IO.FileAccess]::ReadWrite, 
                                            [System.IO.FileShare]::None
                      )
        $TargetFile.Close()  
        Remove-Item -Path $PSItem -WhatIf 
    }
    catch [System.Management.Automation.ItemNotFoundException]{$null = $PSItem.Exception.Message}
    catch {$null = $PSItem.Exception.Message}
}
# Results
<#
What if: Performing the operation "Remove File" on target "D:\temp\abc.txt".
#>