如果给定的文件或目录被锁定(由任何进程使用),如何检入命令行?

时间:2012-05-09 14:27:50

标签: command-line batch-file windows-xp filelock

在尝试对此类文件做任何事情之前,我需要知道。

10 个答案:

答案 0 :(得分:58)

不确定锁定的目录(Windows有吗?)

但是检测文件是否被其他进程写入并不困难。

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

我使用另一个窗口中的以下测试脚本来锁定文件。

(
  >&2 pause
) >> test.txt

当我从一个窗口运行第二个脚本然后从第二个窗口运行第一个脚本时,我收到“锁定”消息。在第一个窗口中按<Enter>后,如果我重新运行第一个脚本,则会收到“未锁定”消息。

<强> 解释

每当将命令的输出重定向到文件时,必须打开文件以进行写访问。即使命令没有产生任何输出,Windows CMD会话也会尝试打开该文件。

>>重定向运算符以追加模式打开文件。

因此>>test.txt echo off将尝试打开文件,它不会向文件写入任何内容(假设echo已经关闭),然后它会关闭文件。该文件不会以任何方式修改。

大多数进程在打开文件进行写访问时都会锁定文件。 (有OS系统调用允许打开文件以共享模式写入,但这不是默认设置)。因此,如果另一个进程已经锁定“test.txt”进行写入,则重定向将失败,并将以下错误消息发送到stderr - “进程无法访问该文件,因为它正由另一个进程使用。” / em>的。在重定向失败时也会生成错误代码。如果命令和重定向成功,则返回成功代码。

只需在命令中添加2>nul就不会阻止错误消息,因为它会重定向命令的错误输出,而不是重定向。这就是为什么我将命令括在括号中,然后将错误输出重定向到pare之外的nul。

因此错误消息被有效隐藏,但错误代码仍然在parens之外传播。标准Windows &&||运算符用于检测parens中的命令是成功还是失败。据推测,echo off永远不会失败,因此失败的唯一可能原因是重定向失败。很可能由于锁定问题而失败,但从技术上讲,可能还有其他原因导致失败。

除非使用||运算符,否则Windows在重定向失败时不会将%ERRORLEVEL%动态变量设置为错误,这是一个奇怪的“功能”。 (见File redirection in Windows and %errorlevel%)。因此||运算符必须在某个较低级别读取返回的错误代码,而不是通过%ERRORLEVEL%变量读取。

使用这些技术检测重定向失败在批处理上下文中非常有用。它可用于建立允许在并行进程中序列化多个事件的锁。例如,它可以使多个进程在“相同”时间安全地写入同一个日志文件。 How do you have shared log files under Windows?


修改

关于锁定的文件夹。我不确定Windows是如何实现这一点的,也许是锁定。但是,如果进程具有涉及该文件夹的活动目录,则无法重命名该文件夹。可以使用

轻松检测到
2>nul ren folderName folderName && echo Folder is NOT locked || echo folder is LOCKED

修改

我已经了解到(call ) (带空格)是一个非常快速的命令,没有副作用,保证在ERRORLEVEL设置为0时成功。(call) (没有空格)是一个没有副作用的快速命令,保证ERRORLEVEL 1失败。

所以我现在使用以下内容检查文件是否被锁定:

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

答案 1 :(得分:10)

除了来自dbenham的great answer之外,以下表格最终帮助我理解使用过的技术:

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

type nul命令给出一个空输出,不会像原始的echo off命令那样影响当前的回显设置。

如果您想使用if–then–else条件记住正确的顺序 - 成功语句(&&)将首先出现,而备用语句(||)将成为第二个:

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

答案 2 :(得分:7)

如果您下载并安装Windows Server 2003资源工具包工具,则会有一个名为oh.exe的实用程序,它将列出给定文件的打开文件句柄:

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

安装后,重启机器,即可使用该实用程序。您可以在“帮助和支持”中心查看所有选项,也可以在命令提示符下键入oh /?

(来自:http://windowsxp.mvps.org/processlock.htm的信息)

答案 3 :(得分:1)

请注意,写入指示文件状态的消息不如设置返回代码的批处理命令有帮助。例如,如果文件被锁定,则返回代码1。

@echo off
2>nul (
   >>test.tmp echo off
) && (EXIT /B 0) || (EXIT /B 1) 

答案 4 :(得分:1)

其他答案给我带来了副作用。例如,the documentation中的以下内容将导致文件监视程序触发:

source

this answer中的以下内容将覆盖对目标文件的所有更改:

COPY /B app.exe+NUL app.exe

在现代版本的Windows上,您可以调用Powershell以零副作用实现此任务:

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

即使未锁定文件,它也根本不会修改文件或其元数据。

用法示例

我在自定义git mergetool脚本中使用此方法来合并Excel文件。 git mergetool的工作方式是等待脚本外壳退出,然后检查目标文件是否被修改,并提示“ XX.yyy似乎不变。合并成功[y / n]吗?”如果不是。但是,Excel(至少是我使用的版本)不会为它打开的每个文件产生新的进程。因此,如果Excel已经打开,则脚本将立即退出,并且git将不会检测到对文件的任何更改,从而出现提示。

所以我设计了上面的方法,并按如下方式使用它:

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

答案 5 :(得分:0)

顺便提一下,dbenham的解决方案似乎也是查明进程是否正在运行的有效方法。这是我在以下应用程序中找到的最佳解决方案:

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

答案 6 :(得分:0)

我想与你分享一个基于@ dbenham诀窍

的脚本示例

此脚本的说明:Check_Locked_Files.bat: 此脚本可以扫描并检查可以修改为脚本的一组文件夹中的锁定文件;例如,我选择了要扫描的那组文件夹:

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

输出结果为HTML格式,以提高可读性。

如果文件被锁定,我们会以红色显示,否则我们会以绿色显示。

整个脚本是: 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%"
::**************************************************************

答案 7 :(得分: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

答案 8 :(得分:0)

如果您想在Cygwin Bash中使用它,请使用以下单线:

# 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

答案 9 :(得分:0)

如果是Windows网络共享,则可以尝试使用powershell命令:

Get-SmbOpenFile

例如,以管理员身份在文件服务器上执行命令:

Get-SmbOpenFile | Where-Object -Property Path -match "file.txt"