使用 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 命令删除文件

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

使用 Remove-Item 结合 Get-ChildItem 命令来读取文件和文件夹,并结合强大的 PowerShell 管道,可以使事情变得轻松愉快。

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

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

使用 PowerShell 删除文件

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

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

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

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

在此示例中,下面的代码删除文件夹中的所有文件。Get-ChildItem cmdlet使用-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 cmdlet添加-Recurse开关以递归获取所有文件。

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\<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網絡服務器生成的日誌文件,以釋放磁碟空間。

在這個示例中,有一些位於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:\tempCreationTime值舊於設置閾值的文件。

第一行定義了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 變量中的值可以被傳送到 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擴展名匹配的文件。百分比(%)符號是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 中使用像 Get-ChildItemRemove-Item 这样的内置 cmdlet 来检索和删除文件比使用 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/