使用PowerShell进行高效文件删除:Remove-Item和WMI

保持服务器和系统的自由磁盘空间至关重要。作为管理员,您不会希望陷入“磁盘已满”的情况。为确保您处于良好状态,您应该学习如何使用 PowerShell 删除文件!

在本文中,您将学习如何使用 PowerShell 从系统中删除文件的各种方法。

让我们开始吧!

先决条件

本文使用 PowerShell 示例,如果您计划跟着操作,您将需要以下内容。

  • A computer that is running Windows 10 or above.
  • Windows PowerShell 5.1 或 PowerShell 7.0
  • A script editor such as Visual Studio Code, Atom, or Notepad++.

使用 Remove-Item Cmdlet 删除文件

当您只需使用 PowerShell 删除文件时,您可能会立即了解到 Remove-Item cmdlet。这个 cmdlet 是使用 PowerShell 删除文件的事实标准。

结合 Get-ChildItem cmdlet 读取文件和文件夹以及强大的 PowerShell 管道,可以让事情变得轻松愉快。

相关: Get-ChildItem:列出文件、注册表、证书等

您知道 Remove-Item cmdlet 有一个别名叫做 del 吗?在 PowerShell 中工作时,使用 Remove-Itemdel 将运行相同的命令。

使用 PowerShell 删除文件

第一个最有用的例子是最基本的——即删除单个文件。要仅删除单个文件,您只需要使用下面的命令。下面的代码删除文件C:\temp\random.txt

Remove-Item -Path C:\temp\random.txt

在PowerShell中运行上述代码不会在屏幕上显示任何内容,除非遇到错误。

使用PowerShell删除文件夹中的所有文件

在这个例子中,下面的代码删除文件夹中的所有文件。Get-ChildItem 命令使用 -Path 参数指定了目标文件夹C:\temp-File 参数表示只包括文件。 Get-ChildItem 忽略文件夹。

Get-ChildItem -Path C:\temp -File | Remove-Item -Verbose

输出应该如下屏幕截图所示。

Successfully deleted all files in a folder

使用PowerShell递归删除所有文件

前面的例子只删除了C:\temp文件夹中的文件。如果您需要删除每个子目录中的文件,您需要在 Get-ChildItem 命令中添加 -Recurse 开关以递归获取所有文件。

Get-ChildItem -Path C:\temp -File -Recurse | Remove-Item -Verbose

运行上面的代码会强制PowerShell查看所有子文件夹并检索所有文件列表。下面的输出显示该代码能够删除顶层文件夹中的文件;但是,它未能检索子文件夹中的文件。

Failed to retrieve files in sub-folders

解决长路径问题

上面显示的错误表明PowerShell“找不到路径的一部分”。该错误表明命令尝试访问的路径不存在——这是具有误导性的。

在这种情况下,错误是因为Get-ChildItem尝试读取的路径超过了260个字符的最大路径长度

下面的截图显示路径或目录及其子目录存在,并且一个名为InTooDeep.txt的文本文件位于最底层的子目录中。所有嵌套目录名中的字符组合造成了长路径问题。

Nested directories creating a long path name

在Windows PowerShell 5.1中,有一种解决长路径名问题的方法。解决方法是使用路径的Unicode版本。而不是像这样指定路径 – C:\temp,使用Unicode版本如下 – ‘\\?\C:\temp’用于位于本地的文件夹,或者‘\\?\UNC\<computername>\<share>\Temp’如果文件夹位于UNC路径中。

Get-ChildItem -Path '\\?\C:\temp' -File -Recurse | Remove-Item -Verbose

使用上述修改后适用于长路径名的代码,下面的输出显示PowerShell成功读取了深层嵌套的路径名并删除了文件。

Deleted the file with a long path name

请注意,长路径名问题不影响PowerShell 7.0。使用PowerShell 7.0,无需使用路径的Unicode版本,因为它已经内置了对长路径名的支持。

如果需要删除文件夹,只需从Get-ChildItem cmdlet中删除-File参数,PowerShell应该删除所有项目,包括文件和文件夹。

使用PowerShell删除早于x天的文件

另一个典型的磁盘空间清理示例是删除早于特定天数的文件。此示例适用于删除旧的日志文件,例如由IIS Web服务器生成的日志文件,以释放磁盘空间。

在这个示例中,有些文件在c:\temp目录下已经存在超过14天了。使用下面的脚本,将显示c:\temp目录中每个文件的NameCreationTImeAgeInDays

Get-ChildItem c:\temp | Select-Object Name,CreationTime,@{n='AgeInDays';e={(New-TimeSpan -Start $PSItem.CreationTime).Days}}

如下的截图中可以看到,有些文件已经存在15天,有些文件存在7天,还有一些是0天。

List of files in c:\temp

现在您知道要删除的文件,可以创建一个脚本,只删除早于特定天数的文件——在本例中,早于14天的文件。

在下面的示例脚本中,C:\temp目录中CreationTime值早于设定的阈值的文件将被删除。

第一行定义了Get-ChildItem搜索的路径。路径保存在$path变量中。然后,第二行指定了阈值。 $threshold的值是要删除的文件的天数。

$threshold变量之后的下一行代码将:

  • 获取位于$path变量指定的文件夹中的文件集合。在这个示例中,路径是C:\temp.
  • 将输出过滤,仅包括CreationTime值早于$threshold变量中保存的天数的文件。在这个示例中,阈值是14天。
  • 将经过筛选的文件列表管道传递到Remove-Item值,以执行对这些文件的删除。
$path = 'C:\Temp'
$threshold = 14
Get-ChildItem -Path $path -File | Where-Object {$PSItem.CreationTime -lt (Get-Date).AddDays(-$threshold)} |Remove-Item -Verbose

当您运行上述代码时,您会看到如下所示的输出。

The script deleted the files older than 14 days

请注意,从上面的截图中可以看出,根据详细消息,脚本只删除了14天前的五个文件。要确认14天内创建的文件仍然存在,运行下面的代码再次列出它们。

Get-ChildItem c:\temp | Select-Object Name,CreationTime,@{n='AgeInDays';e={(New-TimeSpan -Start $PSItem.CreationTime).Days}}

下面的结果证实了Remove-Item没有删除较新的文件。

Newer files are left untouched

使用PowerShell匹配和删除文件模式

删除所有文件,不管名称、类型或扩展名如何,这并不总是最佳方法。有时,您需要明确排除或包含删除过程中的某些文件。

下面的示例显示了如何删除与*.LOG文件名匹配的文件。一种方法是直接使用Remove-Item cmdlet,并使用-Include参数,如下所示。

Remove-Item -Path C:\temp\* -Include *.log

另一种方法,也许是谨慎的方法,是首先使用Get-ChildItem收集要删除的文件列表。只有当您对要删除的文件列表感到满意时,才可以最终将集合传递到Remove-Item cmdlet。

例如,下面的代码获取了c:\temp中的*.LOG文件。

Get-ChildItem -Path C:\temp\* -Include *.log

由于上述代码,Get-ChildItem返回匹配*.LOG文件名的文件列表。

Only files matching the *.log filename is returned

但是,如果你想排除文件名中包含数字5的文件,该怎么办?您可以通过添加-Exclude参数来实现,就像下面的代码一样。

Get-ChildItem -Path C:\temp\* -Include *.log -Exclude *5*

由于您排除了包含数字5的文件,结果现在不同了。具体来说,文件File_5.log不再在列表中,如下所示。

The file File_5.log was excluded

当您对代码生成的文件集合已经满意时,您可以将文件集合传递给Remove-Item命令来最终删除这些文件。

Get-ChildItem -Path C:\temp\* -Include *.log -Exclude *5* | Remove-Item -Verbose

运行最终代码后,您将实现仅删除您选择的文件的目标。

Deleting selected files

使用PowerShell中的WMI删除文件

现在您已经很好地理解了如何使用常见的Remove-Item命令来删除文件,让我们进入一个更高级的用例;使用WMI。

PowerShell提供了对WMI的支持。而对WMI的支持意味着可以从PowerShell中调用WMI查询和方法。是的,WMI不仅适用于管理员在Windows早期使用的Visual Basic脚本。

Microsoft在PowerShell 3.0中发布了针对WMI的特定CIM cmdlet。将用于删除文件的CIM cmdlet是Get-CimInstanceInvoke-CimMethod

使用 PowerShell 和 WMI 删除文件

此示例假定您知道要删除的特定文件的路径。使用带有 Cim_DataFile 类的 Get-CimInstance cmdlet 来检索有关要删除的文件的信息,即C:\Temp\random.txt

$file2delete = Get-CimInstance -ClassName Cim_DataFile -Filter "Name = 'C:\Temp\random.txt'"
 $file2delete

在上述代码中,-Filter 参数接受 WQL 格式的查询。使用 WQL 需要转义一些字符,包括反斜杠。而且,由于 WQL 转义字符也是反斜杠,因此会导致双反斜杠字符 – \\

运行上面的代码会产生下面演示中显示的结果。有关 C:\Temp\random.txt 的信息保存到 $file2delete 变量中

Getting a file using WMI query and PowerShell

现在,已检索到文件 C:\Temp\random.txt 的信息,可以将 $file2delete 变量中的结果对象传递给 Invoke-CimMethod cmdlet。Invoke-CimMethod cmdlet 有一个名为 -Name 的参数,表示 Cim_DataFile 类的方法的名称。

$file2delete | Invoke-CimMethod -Name Delete

如下面的截图所示,ReturnValue 显示为 0,这意味着命令成功执行。

File successfully deleted using WMI and PowerShell

要了解可能的 ReturnValue 代码,请参考此链接 – CIM_DataFile 类的 Delete 方法

此外,下面的截图显示,在运行 Invoke-CimMethod Delete() 方法后,CIM 只删除了 C:\Temp\random.txt 文件。它没有删除其他文件。

C:\Temp\random.txt file was deleted

使用 PowerShell 和 WMI 删除文件夹中的所有文件

在下一个示例中,将向您展示如何使用 PowerShell 和 WMI 删除文件夹中的所有文件。与上一个示例中使用的相同的 cmdlet,即 Get-CimInstanceInvoke-CimMethod 将被使用。但是,这次,WQL 查询将检索文件夹中的所有文件,而不仅仅是一个特定的文件。

在下面的代码中,Get-CimInstance cmdlet 检索位于 C:\temp 中的所有文件。如下所示,查询过滤了 Cim_DataFile 类的 DrivePath 属性。

$file2delete = Get-CimInstance -ClassName Cim_DataFile -Filter "Drive = 'c:' AND Path = '\\temp\\'"

运行上述代码会将有关位于 C:\temp 中的文件的检索信息保存在 $file2delete 变量中。查看 $file2delete 变量的值,显示如下输出。

List of all folders found in c:\temp using WMI

现在,存储在 $file2delete 变量中的值可以被传递到 Invoke-CimMethod 来删除 c:\temp 中的所有文件。

$file2delete | Invoke-CimMethod -Name Delete

user:记住,ReturnValue 代码为0表示成功,并且对于调用删除方法的每个文件都将有其自己的ReturnValue代码。

All files in c:\temp deleted using WMI

使用PowerShell和WMI按扩展名删除文件

您在先前的示例中看到了如何删除文件夹中的所有文件,而不考虑扩展名。但是,您还可以根据扩展名控制哪些文件被删除。

您将在下面的代码中注意到,这次查询有一个附加条件(Name LIKE '%.log)。这个附加条件意味着仅返回与.LOG扩展名匹配的文件。百分号(%)符号是WQL LIKE运算符,表示“零个或多个字符的字符串”。 在编程术语中,%相当于通配符,即星号(*)字符。

$file2delete = Get-CimInstance -ClassName cim_datafile `
-Filter "Drive = 'c:' AND Path = '\\temp\\' AND Name LIKE '%.log'"

$file2delete | Invoke-CimMethod -Name Delete

下面的演示显示,在执行上述代码之前,有九个*.LOG文件和仅一个*.TXT文件。代码运行完成后,*.LOG文件被删除,*.TXT文件是文件夹中唯一剩下的文件。

Deleting files by extension using WMI

比较WMI和Remove-Item

到目前为止,在本教程中,您已经全面了解了如何使用PowerShell删除文件。您已了解了Remove-Item和WMI。两者执行的功能相似,但方法大不相同。

您应该使用哪种方法删除文件;Remove-Item还是WMI?

使用PowerShell中的内置cmdlet,如Get-ChildItemRemove-Item,检索和删除文件比使用WMI要快得多。

下面的示例显示了在使用WMI和内置PowerShell cmdlet获取位于C:\windows\web目录及其子目录下的文件列表时的比较。

## 使用Get-ChildItem递归列出C:\Windows\Web\中的所有文件
Measure-Command { Get-ChildItem C:\Windows\Web\ -Recurse}

## 使用Get-CimInstance和WMI查询递归列出C:\Windows\Web\中的所有文件
Measure-Command { Get-CimInstance -ClassName Cim_DataFile -Filter "Drive = 'c:' AND Path = '\windows\web\%'"}

当在PowerShell中运行上述代码时,您将看到类似下面的输出。

Get-ChildItem vs. Get-CimInstance with WMI Query

从上面的输出中可以看出,使用Get-CimInstance列出C:\windows\web中的文件几乎比使用Get-ChildItem完成的时间长十倍

另外,您是否注意到Get-ChildItem行比Get-CimInstance行要短得多?不仅使用Get-ChildItem执行更快,而且您还从更简洁和更短的代码中受益。

下一步

在本文中,您已经了解了使用内置cmdlet和WMI/CIM删除文件的两种不同方法。

请注意,您应始终使用Get-ChildItemRemove-Item cmdlet来删除文件。与使用WMI相比,这些内置cmdlet更灵活、更容易使用,速度更快。

尝试编写一个可以为您执行磁盘空间清理的脚本。当然,已经存在一些用于此目的的脚本;请尽管将它们作为参考,但如果您愿意练习和学习,您应该尝试创建自己的脚本。

进一步阅读

Source:
https://adamtheautomator.com/powershell-to-delete-files/