Эффективное удаление файлов с помощью 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 нацелена на C:\temp с параметром -Path. Параметр -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 «не удалось найти часть пути». Эта ошибка указывает на то, что путь, который пытается получить команда, не существует – что вводит в заблуждение.

В этом случае ошибка произошла потому, что путь, который пытается прочитать 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-версию пути, потому что в нем уже встроена поддержка длинных имен путей.

Если нужно также удалить папки, просто удалите параметр -File из cmdlet Get-ChildItem, и PowerShell должен удалить все элементы, включая файлы и папки.

Использование PowerShell для удаления файлов старше чем x дней

Еще один типичный пример управления домашним пространством на диске – это удаление файлов, которые старше определенного числа дней. Этот пример полезен для удаления старых файлов журналов, например, тех, которые генерируются веб-серверами IIS, для освобождения места на диске.

В этом примере есть файлы в c:\temp, которые старше 14 дней. Используя приведенный ниже сценарий, для каждого файла в c:\temp показываются Name, CreationTIme и AgeInDays.

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 с использованием параметра -Include, как показано ниже.

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

Еще один способ, возможно, осторожный подход – сначала использовать Get-ChildItem, чтобы собрать список файлов для удаления. Только когда вы удовлетворены списком файлов для удаления, вы наконец перенаправляете коллекцию к командлету Remove-Item.

Например, код ниже получает файлы *.LOG в c:\temp.

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

Удаление файлов с использованием WMI в PowerShell

Теперь, когда у вас есть хорошее понимание использования общего сценария Remove-Item для удаления файлов, давайте перейдем к более продвинутому случаю использования; использование WMI.

PowerShell поставляется с поддержкой WMI. И поддержка WMI означает, что запросы и методы WMI могут вызываться прямо из PowerShell. Да, WMI – это не только для скриптов Visual Basic, которые администраторы использовали в ранние дни Windows.

Майкрософт выпустил специфические для WMI CIM-сценарии в PowerShell 3.0. CIM-сценарии, которые будут использоваться для удаления файлов, – это Get-CimInstance и Invoke-CimMethod.

Используя PowerShell и WMI для удаления файла

Этот пример предполагает, что вы знаете путь к конкретному файлу для удаления. Команда Get-CimInstance с классом 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. У командлета Invoke-CimMethod есть параметр с именем -Name, который представляет собой название метода класса Cim_DataFile.

$file2delete | Invoke-CimMethod -Name Delete

Как видно на скриншоте ниже, значение ReturnValue равно 0, что означает успешное выполнение команды.

File successfully deleted using WMI and PowerShell

Чтобы узнать о возможных кодах возврата ReturnValue, пожалуйста, обратитесь к этой ссылке – Метод Delete класса CIM_DataFile

Кроме того, на скриншоте ниже показано, что после выполнения метода Invoke-CimMethod Delete(), CIM удалил только файл C:\Temp\random.txt. Он не удалил другие файлы.

C:\Temp\random.txt file was deleted

Используя PowerShell и WMI для удаления всех файлов в папке

В следующем примере показано, как удалить все файлы в папке с помощью PowerShell и WMI. Те же самые командлеты, использованные в предыдущем примере, а именно Get-CimInstance и Invoke-CimMethod, будут использоваться. Но на этот раз запрос WQL будет извлекать все файлы в папке, а не только один конкретный файл.

В следующем коде командлет Get-CimInstance извлекает все файлы, находящиеся в C:\temp. Как видно ниже, запрос фильтрует свойства Drive и Path класса Cim_DataFile.

$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

Помните, что код 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-ChildItem и Remove-Item, для извлечения и удаления файлов намного быстрее, чем при использовании WMI.

Ниже приведено сравнение при использовании WMI и встроенной командлеты PowerShell для получения списка файлов в каталоге C:\windows\web и его подкаталогах.

## Перечислить все файлы в C:\Windows\Web\ рекурсивно с помощью Get-ChildItem
Measure-Command { Get-ChildItem C:\Windows\Web\ -Recurse}

## Перечислить все файлы в C:\Windows\Web\ рекурсивно с помощью Get-CimInstance и запроса WMI
Measure-Command { Get-CimInstance -ClassName Cim_DataFile -Filter "Drive = 'c:' AND Path = '\windows\web\%'"}

При выполнении указанного выше кода в PowerShell вы увидите результат, аналогичный приведенному ниже.

Get-ChildItem vs. Get-CimInstance with WMI Query

Как видно из приведенного выше вывода, перечисление файлов в C:\windows\web почти заняло в десять раз больше времени при использовании Get-CimInstance, чем при использовании Get-ChildItem!

Также вы заметили, насколько строка с Get-ChildItem короче, чем с Get-CimInstance? Вы получаете не только более быстрое выполнение при использовании Get-ChildItem, но и выигрываете в чистоте и краткости кода.

Следующие шаги

В этой статье вы увидели два различных способа использования PowerShell для удаления файлов с помощью встроенных командлетов и WMI/CIM.

Запомните, что всегда следует использовать командлеты Get-ChildItem и Remove-Item для удаления файлов. Эти встроенные командлеты более гибкие, удобные и быстрые в использовании, чем при использовании WMI.

Попробуйте разработать скрипт для управления дисковым пространством. Конечно, уже существуют скрипты для этой цели; используйте их в качестве образца, но если вы хотите практиковаться и учиться, вам следует попробовать создать свой собственный.

Дополнительное чтение

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