How to check in command-line if a given file or directory is locked (used by any process)?
Asked Answered
N

10

18

I need to know that before any attempt to do anything with such file.

Narcosis answered 9/5, 2012 at 14:27 Comment(3)
Sorry but you can't do it. From the command line there is no way to know if a file is locked by a process (you have to try to do something with it and "catch" the error).Granitite
I would think powershell could determine this. (Just starting to read Powershell in action, I'm very impressed). In unix, locked files aren't an issue ;^/) Good luck to all.Musteline
@Adriano - not true. See my answer https://mcmap.net/q/245206/-how-to-check-in-command-line-if-a-given-file-or-directory-is-locked-used-by-any-processDisrepute
D
73

Not sure about locked directories (does Windows have that?)

But detecting if a file is being written to by another process is not difficult.

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

I use the following test script from another window to place a lock on the file.

(
  >&2 pause
) >> test.txt

When I run the 2nd script from one window and then run the 1st script from a second window, I get my "locked" message. Once I press <Enter> in the 1st window, I get the "unlocked" message if I rerun the 1st script.

Explanation

Whenever the output of a command is redirected to a file, the file of course must be opened for write access. The Windows CMD session will attempt to open the file, even if the command does not produce any output.

The >> redirection operator opens the file in append mode.

So >>test.txt echo off will attempt to open the file, it writes nothing to the file (assuming echo is already off), and then it closes the file. The file is not modified in any way.

Most processes lock a file whenever they open a file for write access. (There are OS system calls that allow opening a file for writing in a shared mode, but that is not the default). So if another process already has "test.txt" locked for writing, then the redirection will fail with the following error message sent to stderr - "The process cannot access the file because it is being used by another process.". Also an error code will be generated upon redirection failure. If the command and the redirection succeed, then a success code is returned.

Simply adding 2>nul to the command will not prevent the error message because it redirects the error output for the command, not the redirection. That is why I enclose the command in parentheses and then redirect the error output to nul outside of the parens.

So the error message is effectively hidden, but the error code is still propagated outside of the parens. The standard Windows && and || operators are used to detect whether the command inside the parens was successful or failed. Presumably echo off will never fail, so the only possible reason for failure would be the redirection failed. Most likely it fails because of a locking issue, though technically there could be other reasons for failure.

It is a curious "feature" that Windows does not set the %ERRORLEVEL% dynamic variable to an error upon redirection failure unless the || operator is used. (See File redirection in Windows and %errorlevel%). So the || operator must read the returned error code at some low level, not via the %ERRORLEVEL% variable.

Using these techniques to detect redirection failure can be very useful in a batch context. It can be used to establish locks that allow serialization of multiple events in parallel processes. For example, it can enable multiple processes to safely write to the same log file at the "same" time. How do you have shared log files under Windows?


EDIT

Regarding locked folders. I'm not sure how Windows implements this, perhaps with a lock. But if a process has an active directory involving the folder, then the folder cannot be renamed. That can easily be detected using

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

EDIT

I have since learned that (call ) (with a space) is a very fast command without side effects that is guaranteed to succeed with ERRORLEVEL set to 0. And (call) (without a space) is a fast command without side effects that is guaranteed to fail with ERRORLEVEL 1.

So I now use the following to check if a file is locked:

2>nul (
  >>test.txt (call )
) && (echo file is not locked) || (echo file is locked)
Disrepute answered 9/5, 2012 at 16:54 Comment(13)
+1 for the pure cmd syntax (I'm still trying to understand how and why it works!!!)Granitite
@Adriano - Explanation added :-)Disrepute
the (call ) thing does not work for me on winXP it always gives me 'not locked'Grabble
@Grabble - I don't know what you are doing wrong, but I have used the posted code on XP before and it worked perfectly.Disrepute
@Disrepute i only tried it on the cmd. may be its different when used in a batch. the first seems to work also on command lineGrabble
@Disrepute Hi. I tried to measure the time between (echo off) and (call ). Echo is faster by 300 millisecs here on my machine. When you say "guaranteed to succeed" does this mean (echo off) does not work all the time?Canny
@LuigiMackenzieC.Brito - If all I want to do is clear the ERRORLEVEL, then (call ) is good because it has no side effects. But echo off has a side effect in that it turns ECHO off, if it is not off already. This could be a problem.Disrepute
@Disrepute I want to thank you for this nice trick of (call ) and i use it in my batch script called Check_Locked_Files.batWershba
How to get the process id of the locked file, after positive confirmation, in this same script ?Zareba
Here is a batch file test_lock.bat that you can call test_lock file_path and then test for %errorlevel%==1: @echo off {enter} 2>nul ( {enter} >>%1 (call ) {enter} ) && (echo Not locked && exit /B 0) || (echo Locked && exit /B 1)Tramel
Trying this on a file playing through and thus locked by VLC, will fail and report not locked. You cant delete the file but you can write to it. I wasted sooooooo much time on this ;PPromulgate
@Promulgate - Sorry for your frustration. I can see how you could be misled given that the question asks for determination if a file is locked, without specifying a specific type of lock, and my answer was accepted. However, my answer is pretty clear that it is restricted to detecting if a file is locked for write access.Disrepute
This is very useful! In particular, I like the rename option, which sets ERRORLEVEL and is thus easy to check. I found it works for individual files as well, as long as you obey the 'ren' command's rules (can have full directory path in 1st parameter but not in 2nd).Chasteen
N
10

In addition to great answer from dbenham, the following form finally help me understand used technique:

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

type nul command gives an empty output and does not affect the current echo setting like echo off command in orginal.

If you want to use if–then–else condition remember of correct order - success statement (&&) is going first and alternate statement (||) is going second:

command && (echo Command is successful) || (echo Command has failed) 
Narvaez answered 17/1, 2013 at 22:5 Comment(1)
Beware with the if && then || else format, remember that the else block will also be executed if the then block fails.Responsum
D
7

If you download and install the Windows Server 2003 Resource Kit Tools there is a utility called oh.exe that will list open file handles for a given file:

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

Once you install it, reboot your machine and you'll be able to use the utility. You can see all the options in the Help and Support center as well as by typing oh /? in the command prompt.

(Info from : http://windowsxp.mvps.org/processlock.htm )

Deprave answered 9/5, 2012 at 14:48 Comment(0)
D
2

Note, the writing of a message stating the file status was less helpful than a batch command that set a return code. For example, return code 1 if file is locked.

@echo off
2>nul (
   >>test.tmp echo off
) && (EXIT /B 0) || (EXIT /B 1) 
Duodecimo answered 13/5, 2016 at 17:43 Comment(0)
F
2

Other answers resulted in side-effects for me. For instance, the following from this answer will cause file watchers to trigger:

COPY /B app.exe+NUL app.exe

And the following from the top answer here would overwrite any changes made to the target file:

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

On modern version of Windows, you can call into Powershell to accomplish this task with zero side-effects:

powershell -Command "$FileStream = [System.IO.File]::Open('%FILE%', 'Open', 'Write'); $FileStream.Close(); $FileStream.Dispose()" && (echo File is not locked) || (echo File is locked)

This will not modify the file or its metadata at all, even when it isn't locked.

Example usage

I use this method in my custom git mergetool script for merging Excel files. The way a git mergetool works is that it waits for the script shell to exit, then checks if the target file was modified, prompting with "XX.yyy seems unchanged. Was the merge successful [y/n]?" if it wasn't. However, Excel (at least the version I'm using) does not spawn a new process for each file it opens. So if Excel is already open, the script will exit immediately, and git will detect no changes to the file, resulting in that prompt.

So I devised the method above, and I use it like below:

REM block until MERGED is closed
:loop
powershell -Command "$FileStream = [System.IO.File]::Open('%MERGED%', 'Open', 'Write'); $FileStream.Close(); $FileStream.Dispose()" >NUL 2>NUL || (goto :loop)
Fruity answered 9/3, 2020 at 19:11 Comment(1)
This answer worked best, in my opinion. It does not try to do anything to the file which could disturb the system. Use with the >NUL 2>NULL before the echo statements to avoid the ugliness of error messages.Sicular
H
0

Incidentally, dbenham's solution also seems to be an effective way to find out if a process is running. It was the best solution I found for the following application:

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
Highkeyed answered 19/11, 2015 at 20:32 Comment(0)
W
0

Just i want to share with you an example of my script based on @dbenham's trick

Description of this script : Check_Locked_Files.bat : This script can scan and check for locked files on a set of folders that can be modified into the script; for example, i have chosen those set of folders to be scanned :

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%" 

The output result is in HTML format for more readability.

If the file is locked we show it in red color otherwise we show it in green color.

And the whole script is : 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%"
::**************************************************************
Wershba answered 11/8, 2017 at 11:27 Comment(0)
M
0
:: 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
Mccafferty answered 8/9, 2017 at 20:50 Comment(0)
W
0

In case you want to use this in a Cygwin Bash, here are the 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
Wacker answered 22/11, 2018 at 10:54 Comment(0)
W
0

In case of windows network share you can try powershell command:

Get-SmbOpenFile

For example execute on file server command as administrator:

Get-SmbOpenFile | Where-Object -Property Path -match "file.txt"
Westphalia answered 2/11, 2020 at 8:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.