Эффективное удаление файлов с помощью 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 дней. С использованием скрипта ниже показывается Name, CreationTIme и AgeInDays каждого файла в c:\temp.

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.

Microsoft выпустила специфические для 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-to-delete-files/