使用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 pipeline,使用 Remove-Item 真的可以让事情变得轻松。

相关: 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文件夹中的文件。如果您需要还删除每个子目录中的文件,则需要将-Recurse开关添加到Get-ChildItem命令中以递归获取所有文件。

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

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

Failed to retrieve files in sub-folders

解决长路径问题

上面显示的错误表明PowerShell“找不到路径的一部分”。该错误表明cmdlet尝试到达的路径不存在 – 这是误导性的。

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

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

Nested directories creating a long path name

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

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脚本。

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

使用 PowerShell 和 WMI 删除文件

此示例假定您知道要删除的特定文件的路径。使用 Get-CimInstance cmdlet 与 Cim_DataFile 类来检索要删除的文件的信息,即 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-CimMethodDelete() 方法后,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 变量中的值可以传输到 c:\temp 中删除所有文件的 Invoke-CimMethod

$file2delete | Invoke-CimMethod -Name Delete

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

All files in c:\temp deleted using WMI

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

在前面的示例中,您已经看到如何删除文件夹中的所有文件,而不考虑扩展名。但是,您也可以根据扩展名控制要删除的文件。

您会注意到下面的代码中,这次查询增加了一个条件(Name LIKE '%.log)。这个添加的条件意味着只返回与.LOG扩展名匹配的文件。百分号(%)符号是WQLLIKE操作符,意思是“零个或多个字符的字符串”。在编程术语中,%相当于通配符,即星号(*)字符。

$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不仅可以获得更快的执行速度,还可以获得更清洁和更短的代码。

下一步

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

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

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

进一步阅读

Source:
https://adamtheautomator.com/powershell-delete-file/