web-dev-qa-db-fra.com

Comment vérifier en ligne de commande si un fichier ou un répertoire donné est verrouillé (utilisé par n'importe quel processus)?

J'ai besoin de le savoir avant toute tentative de faire quoi que ce soit avec un tel fichier.

14
rsk82

Pas sûr des répertoires verrouillés (Windows en a-t-il?)

Mais détecter si un fichier est en cours d'écriture par un autre processus n'est pas difficile.

@echo off
2>nul (
  >>test.txt echo off
) && (echo file is not locked) || (echo file is locked)

J'utilise le script de test suivant à partir d'une autre fenêtre pour verrouiller le fichier.

(
  >&2 pause
) >> test.txt

Lorsque j'exécute le deuxième script à partir d'une fenêtre, puis que je lance le premier script à partir d'une deuxième fenêtre, le message "verrouillé" s'affiche. Une fois que j’ai appuyé sur <Enter> dans la 1ère fenêtre, j’obtiens le message "déverrouillé" si je relance le premier script.

Explication

Chaque fois que la sortie d'une commande est redirigée vers un fichier, le fichier doit évidemment être ouvert pour un accès en écriture. La session Windows CMD tentera d'ouvrir le fichier, même si la commande ne produit aucun résultat.

L'opérateur de redirection >> ouvre le fichier en mode ajout.

Donc, >>test.txt echo off tentera d'ouvrir le fichier, il n'écrira rien dans le fichier (en supposant que l'écho est déjà désactivé), puis ferme le fichier. Le fichier n'est en aucun cas modifié.

La plupart des processus verrouillent un fichier lorsqu'ils ouvrent un fichier pour un accès en écriture. (Il existe des appels système du système d'exploitation qui permettent d'ouvrir un fichier en écriture en mode partagé, mais ce n'est pas le cas par défaut). Donc, si un autre processus a déjà "test.txt" verrouillé en écriture, la redirection échouera avec le message d'erreur suivant envoyé à stderr - "Le processus ne peut pas accéder au fichier car il est utilisé par un autre processus.". De plus, un code d'erreur sera généré en cas d'échec de la redirection. Si la commande et la redirection réussissent, un code de réussite est renvoyé.

Ajouter simplement 2>nul à la commande n'empêchera pas le message d'erreur car il redirige la sortie d'erreur pour la commande, pas la redirection. C'est pourquoi je place la commande entre parenthèses, puis je redirige la sortie d'erreur vers zéro en dehors des parenthèses.

Ainsi, le message d'erreur est effectivement masqué, mais le code d'erreur est toujours propagé en dehors des parenthèses. Les opérateurs Windows && et || standard sont utilisés pour détecter si la commande dans les parens a été réussie ou a échoué. Vraisemblablement, echo off n'échouera jamais, la seule raison possible de cet échec serait donc l'échec de la redirection. Il est fort probable qu'il échoue en raison d'un problème de verrouillage, même si techniquement, il peut exister d'autres raisons de l'échec.

Il est curieux de constater que Windows ne définit pas la variable dynamique% ERRORLEVEL% sur une erreur en cas d’erreur de redirection, à moins que l’opérateur || ne soit utilisé. (Voir Redirection de fichier sous Windows et% errorlevel% ). Ainsi, l'opérateur || doit lire le code d'erreur renvoyé à un niveau bas, et non via la variable% ERRORLEVEL.

L'utilisation de ces techniques pour détecter une défaillance de redirection peut s'avérer très utile dans un contexte de traitement par lots. Il peut être utilisé pour établir des verrous permettant la sérialisation de plusieurs événements dans des processus parallèles. Par exemple, il peut permettre à plusieurs processus d’écrire en toute sécurité dans le même fichier journal au même moment. Comment avez-vous partagé les fichiers journaux sous Windows?


MODIFIER

En ce qui concerne les dossiers verrouillés. Je ne suis pas sûr de savoir comment Windows implémente cela, peut-être avec un verrou. Mais si un processus a un répertoire actif impliquant le dossier, celui-ci ne peut pas être renommé. Cela peut facilement être détecté en utilisant

2>nul ren folderName folderName && echo Folder is NOT locked || echo folder is LOCKED

MODIFIER

Depuis, j’ai appris que (call )(avec un espace)} est une commande très rapide sans effets secondaires dont le succès est garanti avec ERRORLEVEL défini sur 0. Et (call)(sans espace)} __ une commande rapide sans effets secondaires dont l’échec est garanti avec ERRORLEVEL 1.

J'utilise donc les éléments suivants pour vérifier si un fichier est verrouillé:

2>nul (
  >>test.txt (call )
) && (echo file is not locked) || (echo file is locked)
57
dbenham

En plus de excellente réponse de dbenham, le formulaire suivant m'aide enfin à comprendre la technique utilisée:

( type nul >> file.txt ) 2>nul || echo File is locked!

La commande type nul donne une sortie vide et n’affecte pas le paramètre d’écho actuel comme la commande echo off dans la version originale.

Si vous souhaitez utiliser la condition if–then–else, rappelez-vous de l'ordre correct - l'instruction de réussite (&&) est exécutée en premier et l'instruction alternative (||) en deuxième position:

command && (echo Command is successful) || (echo Command has failed) 
10
lukk

Si vous téléchargez et installez les outils du kit de ressources Windows Server 2003, un utilitaire appelé oh.exe répertorie les descripteurs de fichiers ouverts pour un fichier donné:

http://www.Microsoft.com/en-us/download/details.aspx?id=17657

Une fois que vous l'avez installé, redémarrez votre ordinateur et vous pourrez utiliser l'utilitaire. Vous pouvez voir toutes les options dans le centre d’aide et de support, ainsi qu’en tapant oh /? dans la commande Invite.

(Infos de: http://windowsxp.mvps.org/processlock.htm )

7
Justin Pearce

Notez que l'écriture d'un message indiquant l'état du fichier était moins utile qu'une commande batch qui définit un code de retour. Par exemple, renvoyez le code 1 si le fichier est verrouillé. 

@echo off
2>nul (
   >>test.tmp echo off
) && (EXIT /B 0) || (EXIT /B 1) 
1
user6331738
:: Create the file Running.tmp

ECHO %DATE% > Running.tmp
ECHO %TIME% >> Running.tmp

:: block it and do the work

(
  >&2 CALL :Work 30
) >> Running.tmp

:: when the work is finished, delete the file
DEL Running.tmp
GOTO EOF

:: put here the work to be done by the batch file

:Work
ping 127.0.0.1 -n 2 -w 1000 > NUL
ping 127.0.0.1 -n %1 -w 1000 > NUL

:: when the process finishes, the execution go back
:: to the line after the CALL
0
user3407604

Juste je veux partager avec vous un exemple de mon script basé sur le tour de @ dbenham

Description de ce script: Check_Locked_Files.bat: Ce script peut analyser et rechercher des fichiers verrouillés dans un ensemble de dossiers pouvant être modifiés dans le script; Par exemple, j'ai choisi cet ensemble de dossiers à analyser:

Set Folders=^
^ "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%UserProfile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%ProgramFiles%\Internet Explorer"^
^ "%ProgramFiles%\Skype"^
^ "%ProgramFiles%\TeamViewer"^
^ "%WinDir%\system32\drivers"^
^ "%Temp%" 

Le résultat en sortie est au format HTML pour plus de lisibilité. 

Si le fichier est verrouillé, nous le montrerons en rouge, sinon nous le montrerons en vert.

Et tout le script est:Check_Locked_Files.bat

@echo off
Rem This source is inspired from here
Rem hxxps://stackoverflow.com/questions/
Rem 10518151/how-to-check-in-command-line-if-a-given-file-or-directory-is-locked-used-by-any?answertab=active#tab-top
Rem Thanks for dbenham for this Nice trick ;)
Mode con cols=90 lines=5 & color 9E
Title Scan and Check for Locked Files by Hackoo 2017 
set "LogFile=%~dp0%~n0.html"
(
    echo ^<html^>
    echo ^<title^> Scan and Check for locked files by Hackoo 2017^</title^>
    echo ^<body bgcolor^=#ffdfb7^>
    echo ^<center^>^<b^>Log Started on %Date% @ %Time% by the user : "%username%" on the computer : "%ComputerName%"^</b^>^</center^>
)> "%LogFile%"
echo(
echo       --------------------------------------------------------------------------
echo         Please Wait a while ....... Scanning for locked files is in progress 
echo       --------------------------------------------------------------------------
Rem We Play radio just for fun and in order to let the user be patient until the scan ended
Call :Play_DJ_Buzz_Radio
Timeout /T 3 /nobreak>nul
cls
Set Folders=^
^ "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%UserProfile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%ProgramFiles%\Internet Explorer"^
^ "%ProgramFiles%\Skype"^
^ "%ProgramFiles%\TeamViewer"^
^ "%WinDir%\system32\drivers"^
^ "%Temp%" 

@For %%a in (%Folders%) Do (
    ( echo ^<hr^>^<font color^=DarkOrange^>^<B^>Folder : %%a^</B^>^</font^>^<hr^>) >> "%LogFile%"
    @for /f "delims=" %%b in ('Dir /A-D /s /b "%%~a\*.*"') do (
        Call :Scanning "%%~nxb"
        Call:Check_Locked_File "%%~b" "%LogFile%"
    )
)

(
    echo ^<hr^>
    echo ^<center^>^<b^>Log ended on %Date% @ %Time% on the computer : "%ComputerName%"^</b^>^</center^>
    echo ^</body^>
    echo ^</html^>
)>> "%LogFile%"
Start "" "%LogFile%" & Call :Stop_Radio & exit
::***********************************************************************************
:Check_Locked_File <File> <LogFile>
(
    2>nul (
    >>%1 (call )
    ) && ( @echo ^<font color^=green^>file "%~1"^</font^>^<br^>
    ) || ( 
        @echo ^<font color^=red^>file "%~1" is locked and is in use^</font^>^<br^> 
    ) 
)>>%2 2>nul
exit /b
::***********************************************************************************
:Scanning <file>
cls
echo(
echo       --------------------------------------------------------------------------
echo          Please Wait a while... Scanning for %1
echo       --------------------------------------------------------------------------
exit /b
::***********************************************************************************
:Play_DJ_Buzz_Radio
Taskkill /IM "wscript.exe" /F >nul 2>&1
Set "vbsfile=%temp%\DJBuzzRadio.vbs"
Set "URL=http://www.chocradios.ch/djbuzzradio_windows.mp3.asx"
Call:Play "%URL%" "%vbsfile%"
Start "" "%vbsfile%"
Exit /b
::**************************************************************
:Play
(
echo Play "%~1"
echo Sub Play(URL^)
echo    Dim Sound
echo    Set Sound = CreateObject("WMPlayer.OCX"^)
echo    Sound.URL = URL
echo    Sound.settings.volume = 100
echo    Sound.Controls.play
echo    do while Sound.currentmedia.duration = 0
echo        wscript.sleep 100
echo    loop
echo    wscript.sleep (int(Sound.currentmedia.duration^)+1^)*1000
echo End Sub
)>%~2
exit /b
::**************************************************************
:Stop_Radio
Taskkill /IM "wscript.exe" /F >nul 2>&1
If Exist "%vbsfile%" Del "%vbsfile%"
::**************************************************************
0
Hackoo

Incidemment, la solution de dbenham semble également être un moyen efficace de savoir si un processus est en cours d'exécution. C'était la meilleure solution que j'ai trouvée pour l'application suivante:

start /b "job1.exe >> job1.out"
start /b /wait "job2.exe >> job2.out"   

::wait for job1 to finish using dbenham's code to check if job1.out is in use

comparejobs.exe
0
John

Si vous souhaitez utiliser ceci dans un Cygwin Bash, voici les one-liners: 

# To lock a file: (in a different window)
cmd.exe /C "( >&2 pause ) >> test.txt"
#Press any key to continue . . .

# To test if a file is locked (with text)
cmd.exe /C "2>nul ( >>test.txt (call ) ) && (echo ok) || (echo locked)"
#locked

# To test if a file is locked (with POSIX exit code)
cmd.exe /C "2>nul ( >>test.txt (call ) ) && (exit /b 0) || (exit /b 1)"
echo $?
#1
0
not2qubit