Agence web » Actualités du digital » Comment gérer les descripteurs de fichiers ouverts avec PowerShell

Comment gérer les descripteurs de fichiers ouverts avec PowerShell

Julia Tim / Shutterstock

L'une des erreurs les plus frustrantes qu'un utilisateur final ou un administrateur informatique peut traiter est celle des fichiers verrouillés dans Windows. Lorsque vous supprimez un dossier, déplacez un fichier ou modifiez une configuration et que vous rencontrez un message d'erreur de fichier verrouillé, il est préférable de traiter cela rapidement et efficacement.

Microsoft a présenté PowerShell en tant que shell de remplacement, mais il a beaucoup plus de fonctionnalités que cela et est un langage complexe et capable. Voyons dans cet article comment utiliser PowerShell pour gérer les fichiers verrouillés.

Le problème des fichiers verrouillés

Comment un fichier est-il verrouillé exactement? Lors d'une utilisation normale, un processus crée de nombreux descripteurs de ressources telles qu'un fichier. Ce faisant, les processus verrouillent souvent le fichier pour empêcher les modifications de configuration involontaires ou autres altérations. Le problème est souvent qu'il est difficile de déterminer quel processus a verrouillé le fichier et, par la suite, comment supprimer ce verrou du fichier.

Malheureusement, il n'y a pas d'applet de commande intégrée pour tester un fichier et dire s'il est verrouillé ou par quel processus. Par conséquent, vous devez créer vos propres fonctions ou envelopper d'autres outils utiles qui existent pour vous aider à en savoir plus sur ces fichiers.

Test des fichiers verrouillés

Sous Windows, vous pouvez tester pour voir si un fichier individuel est verrouillé. En utilisant le bloc de code suivant, vous pouvez tester pour voir si un fichier donné est verrouillé. le $Item La variable doit être définie sur un chemin de fichier complet. En testant pour voir si le fichier peut être ouvert pour l'écriture, comme vu avec le (System.IO.File)::Open($Item,'Open','Write') , vous pouvez savoir si le fichier est verrouillé.

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

J'ai dit que Windows n'avait pas de fonction intégrée, mais il y a un cas où une fonction existe. Si vous avez un partage distant ou même des partages administratifs (tels que c$), vous pouvez utiliser le Get-SMBOpenFile applet de commande pour signaler ces fichiers ouverts.

PS C:> Get-SMBOpenFile

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

PS C:> 

L'inconvénient est que cela ne fonctionne que pour les fichiers accessibles à distance. Aucun fichier verrouillé utilisé sur votre système local ne sera signalé. Par conséquent, dans la plupart des cas, ce n'est pas une solution viable. Pour fermer, vous pouvez diriger les fichiers ouverts renvoyés vers le Close-SMBOpenFile commander.

Get-SMBOpenFile | Close-SMBOpenFile

Utilitaire OpenFiles

Windows a un utilitaire intégré nommé openfiles qui peut aider à répertorier les fichiers utilisés et à les déconnecter. À première vue, il semble parfait pour vos besoins! Vous pouvez même envelopper cela dans une fonction PowerShell pour faciliter l'interrogation et la déconnexion des fichiers.

Ouvrez une invite d'administration PowerShell et exécutez la commande openfiles /query. Tout de suite, vous devriez recevoir un message d'erreur indiquant que l'indicateur global «maintenir la liste des objets» doit être activé.

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.

Cette liste d'objets est ce qui maintient réellement la liste des poignées en cours d'utilisation et permet openfiles pour interroger ces informations. Pour l'activer, entrez openfiles /local on puis redémarrez votre ordinateur. L'inconvénient de l'activation de cette fonctionnalité est qu'il y a une légère baisse des performances, qui, selon votre système, peut ne pas valoir l'utilité de l'utilisation de cet outil. Cela étant dit, voyons comment nous pouvons faire en sorte que cela fonctionne dans 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.

Avec les exemples précédents, vous pouvez voir comment importer la sortie CSV de openfiles dans PowerShell. En utilisant ces informations, vous pouvez ensuite disconnect un fichier pour le déverrouiller. En raison de la baisse des performances, vous risquez de devoir activer maintain objects list capacité, cela pourrait ne pas valoir la peine pour vos besoins. Pour cette raison, d'autres solutions peuvent être nécessaires.

L'application Poignée

Sysinternals est connu pour les nombreux outils informatiques utiles et presque essentiels qu'il fabrique. Il y a quelque temps, Sysinternals a été acquis par Microsoft, et vous pouvez télécharger et utiliser ces outils bien pris en charge par vous-même. Idéalement, il existe une application nommée handles qui fournit exactement ce que vous cherchez!

Tout d'abord, vous devez télécharger l'application, décompressez les fichiers et placez les exécutables dans un emplacement que votre variable d'environnement Path a inclus. Ce faisant, vous pouvez facilement référencer l'application où vous en avez besoin. En utilisant une simple requête pour les fichiers ouverts, vous pouvez voir que vous obtenez beaucoup de résultats (tronqués pour plus de lisibilité).

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
...

Vous semblez obtenir ce que vous voulez – au moins un moyen de savoir quels fichiers sont utilisés – et vous pouvez les tester en utilisant votre code verrouillé de fichier d'avant. Mais comment rendez-vous cela plus facile à utiliser? Le code suivant lit chaque processus et récupère uniquement les fichiers verrouillés. L'inconvénient est que cela prend du temps car il existe de nombreux processus.

$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
        }
    }
}

En fin de compte, ce que vous obtenez est une collection de fichiers exploitables, répertoriés par processus, dont vous savez qu'ils sont en cours d'utilisation et peuvent être filtrés davantage. Si vous découvrez que vous devez fermer l'un d'eux, vous pouvez effectuer les opérations suivantes (en tant qu'administrateur):

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.

Vous pouvez envelopper tout cela dans une fonction pour le rendre encore plus facile à analyser et à rechercher si nécessaire. De nombreuses possibilités existent, notamment en combinant les différentes méthodes en une solution adaptée à votre environnement.

Conclusion

Traiter les fichiers verrouillés peut être un défi, surtout quand il arrête ce dont vous avez besoin pour être fait rapidement. Il existe un certain nombre de façons de rechercher et de déverrouiller ces fichiers, mais cela nécessite un peu de travail car Windows n'a pas de méthode intégrée vraiment complète pour gérer ces fichiers verrouillés. Les solutions décrites devraient permettre de résoudre rapidement le problème et de vous laisser passer à des tâches beaucoup plus importantes!

★★★★★