Поиск по сайту:

Как управлять дескрипторами открытых файлов с помощью PowerShell


Одна из самых неприятных ошибок, с которой может столкнуться конечный пользователь или ИТ-администратор, — это заблокированные файлы в Windows. Когда вы удаляете папку, перемещаете файл или редактируете конфигурацию и сталкиваетесь с сообщением об ошибке заблокированного файла, лучше справиться с этим быстро и эффективно.

Microsoft представила PowerShell в качестве замены оболочки, но она обладает гораздо большей функциональностью и представляет собой сложный и многофункциональный язык. Давайте рассмотрим в этой статье, как вы можете использовать PowerShell для работы с заблокированными файлами.

Проблема с заблокированным файлом

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

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

Проверка заблокированных файлов

В Windows вы можете проверить, заблокирован ли отдельный файл. Используя следующий блок кода, вы можете проверить, заблокирован ли данный файл. В переменной $Item должен быть указан полный путь к файлу. Проверив, можно ли открыть файл для записи, как показано с помощью команды [System.IO.File]::Open($Item,Open,Write), вы можете определить, может ли файл заблокирован.

If ([System.IO.File]::Exists($Item)) {
  Try {
      $FileStream = [System.IO.File]::Open($Item,'Open','Write')

      $FileStream.Close()
      $FileStream.Dispose()

      $IsLocked = $False
  } Catch [System.UnauthorizedAccessException] {
      $IsLocked = 'AccessDenied'
  } Catch {
      $IsLocked = $True
  }
}

Get-SMBOpenFile

Я сказал, что в Windows нет встроенной функции, но есть один случай, когда функция существует. Если у вас есть удаленный общий ресурс или даже административный общий ресурс (например, c$), вы можете использовать командлет Get-SMBOpenFile для создания отчетов об этих открытых файлах.

PS C:> Get-SMBOpenFile

FileId       SessionId    Path  ShareRelativePath
------       ---------    ----  -----------------
154618822665 154618822657 C:

PS C:> 

Недостатком является то, что это работает только для файлов, к которым осуществляется удаленный доступ. О любых заблокированных файлах, которые используются в вашей локальной системе, не будет сообщено, поэтому в большинстве случаев это нежизнеспособное решение. Чтобы закрыть, вы можете передать открытые файлы, возвращенные команде Close-SMBOpenFile.

Get-SMBOpenFile | Close-SMBOpenFile

Утилита OpenFiles

В Windows есть встроенная утилита с именем openfiles, которая может помочь составить список используемых файлов и отключить их. На первый взгляд, он идеально подходит для ваших нужд! Вы даже можете обернуть это в функцию PowerShell, чтобы упростить запрос и отключение файлов.

Откройте административную командную строку PowerShell и выполните команду openfiles /query. Сразу же вы должны получить сообщение об ошибке, в котором говорится, что глобальный флаг «сохранить список объектов» должен быть включен.

PS C:/> openfiles /query

INFO: The system global flag 'maintain objects list' needs
      to be enabled to see local opened files.
      See Openfiles /? for more information.


Files opened remotely via local share points:
---------------------------------------------

INFO: No shared open files found.

Этот список объектов фактически поддерживает список используемых дескрипторов и позволяет openfiles запрашивать эту информацию. Чтобы включить это, введите openfiles /local on и перезагрузите компьютер. Недостатком включения этой функции является небольшое снижение производительности, которое, в зависимости от вашей системы, может не стоить использования этого инструмента. При этом давайте посмотрим, как мы можем заставить это работать в PowerShell.

PS C:> openfiles /Query /fo csv /nh

Files opened remotely via local share points:
---------------------------------------------
"ID","Accessed By","Type","Open File (Pathexecutable)"
"608","user","Windows","C:"

PS C:> openfiles /Query /fo csv | Select-Object -Skip 4 | ConvertFrom-CSV

ID  Accessed By  Type    Open File (Pathexecutable)
--  -----------  ----    ---------------------------
608 user         Windows C:

PS C:> openfiles /disconnect /id 608

SUCCESS: The connection to the open file "C:" has been terminated.

В предыдущих примерах вы можете увидеть, как импортировать выходные данные openfiles в формате CSV в PowerShell. Используя эту информацию, вы можете отключить файл, чтобы разблокировать его. Из-за падения производительности, которое может возникнуть при включении возможности сохранять список объектов, она может оказаться нецелесообразной для ваших нужд. Из-за этого могут потребоваться другие решения.

Приложение ручки

Компания Sysinternals известна множеством полезных и почти необходимых ИТ-инструментов, которые они создают. Некоторое время назад Microsoft приобрела Sysinternals, и вы можете загрузить и использовать эти хорошо поддерживаемые инструменты для себя. Удобно, что есть приложение под названием handles, которое предоставляет именно то, что вы ищете!

Во-первых, вам необходимо загрузить приложение, разархивировать файлы и поместить исполняемые файлы в папку, указанную вашей переменной окружения Path. Таким образом, вы можете легко ссылаться на приложение, где бы оно вам ни понадобилось. Используя простой запрос для открытых файлов, вы можете увидеть, что вы получаете много результатов (усеченных для удобства чтения).

PS C:/> handle64 -NoBanner
...
------------------------------------------------------------------------------
RuntimeBroker.exe pid: 9860 User
   48: File          C:WindowsSystem32
  188: Section       BaseNamedObjects__ComCatalogCache__
  1EC: Section       BaseNamedObjects__ComCatalogCache__
------------------------------------------------------------------------------
chrome.exe pid: 4628 User
   78: File          C:Program Files (x86)GoogleChromeApplication78.0.3904.108
  1C4: Section       Sessions1BaseNamedObjectswindows_shell_global_counters
...

Кажется, вы получаете то, что хотите — по крайней мере, способ узнать, какие файлы используются, — и вы можете протестировать их, используя ранее заблокированный код. Но как сделать это проще в использовании? Следующий код считывает каждый процесс и извлекает только заблокированные файлы. Недостатком является то, что это занимает некоторое время, так как существует много процессов.

$Processes = Get-Process

$results = $Processes | Foreach-Object {
    $handles = (handle64 -p $_.ID -NoBanner) | Where-Object { $_ -Match " File " } | Foreach-Object {
            [PSCustomObject]@{
        "Hex"  = ((($_ -Split " ").Where({ $_ -NE "" })[0]).Split(":")[0]).Trim()
        "File" = (($_ -Split " ")[-1]).Trim()
        }
    }

    If ( $handles ) {
        [PSCustomObject]@{
            "Name"    = $_.Name
            "PID"     = $_.ID
            "Handles" = $handles
        }
    }
}

В конечном счете, вы получаете набор файлов, отсортированных по процессам, которые, как вы знаете, используются и могут быть дополнительно отфильтрованы. Если вы обнаружите, что вам нужно закрыть один из них, вы можете сделать следующее (как Администратор):

PS C:> $results |
>>  Where-Object Name -EQ 'Notepad' |
>>  Where-Object { $_.Handles.File -Match "test.txt" }

Name                      PID Handles
----                      --- -------
Notepad                   12028 {@{Hex=44; File=C:test.txt}


PS C:> handle64 -p 12028 -c 44 -y -nobanner

44: File  (R-D)   C:test.txt

Handle closed.

Вы можете дополнительно обернуть все это в функцию, чтобы упростить синтаксический анализ и поиск по мере необходимости. Существует множество возможностей, особенно при объединении различных методов в решение, подходящее для вашей среды.

Заключение

Работа с заблокированными файлами может быть сложной задачей, особенно когда это останавливает то, что вам нужно сделать быстро. Существует несколько способов найти и разблокировать эти файлы, но это требует некоторой работы, поскольку в Windows нет действительно всеобъемлющего встроенного метода работы с этими заблокированными файлами. Изложенные решения должны быстро решить любую проблему и позволить вам перейти к гораздо более важным задачам!