如果给定文件或目录被锁定(由任何进程使用),如何检查命令行?
Posted
技术标签:
【中文标题】如果给定文件或目录被锁定(由任何进程使用),如何检查命令行?【英文标题】:How to check in command-line if a given file or directory is locked (used by any process)? 【发布时间】:2012-05-18 02:09:38 【问题描述】:在尝试对此类文件执行任何操作之前,我需要知道这一点。
【问题讨论】:
对不起,你做不到。从命令行无法知道文件是否被进程锁定(您必须尝试对其执行某些操作并“捕获”错误)。 我认为 powershell 可以确定这一点。 (刚开始阅读 Powershell 的实际操作,我印象非常深刻)。在 unix 中,锁定的文件不是问题;^/) 祝大家好运。 @Adriano - 不正确。看我的回答***.com/a/10520609/1012053 【参考方案1】:如果您下载并安装 Windows Server 2003 资源工具包工具,则有一个名为 oh.exe
的实用程序将列出给定文件的打开文件句柄:
http://www.microsoft.com/en-us/download/details.aspx?id=17657
安装后,重新启动您的机器,您就可以使用该实用程序了。您可以在帮助和支持中心查看所有选项,也可以在命令提示符下输入oh /?
。
(信息来自:http://windowsxp.mvps.org/processlock.htm)
【讨论】:
【参考方案2】:不确定锁定的目录(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 已关闭),然后关闭文件。该文件未以任何方式修改。
大多数进程在打开文件进行写访问时都会锁定文件。 (有一些操作系统系统调用允许打开文件以在共享模式下写入,但这不是默认设置)。因此,如果另一个进程已经锁定“test.txt”以进行写入,那么重定向将失败,并发送以下错误消息到 stderr - “该进程无法访问该文件,因为它正在被另一个进程使用。”。重定向失败时也会生成错误代码。如果命令和重定向成功,则返回成功码。
仅将2>nul
添加到命令不会阻止错误消息,因为它重定向命令的错误输出,而不是重定向。这就是为什么我将命令括在括号中,然后将错误输出重定向到括号外的 nul。
因此错误消息被有效地隐藏了,但错误代码仍然传播到括号之外。标准的 Windows &&
和 ||
运算符用于检测括号内的命令是成功还是失败。大概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 表示纯 cmd 语法(我仍在尝试理解它的工作原理和原因!!!) @Adriano - 已添加说明 :-)(call )
在 winXP 上对我不起作用,它总是给我“未锁定”
@vlad_tepesch - 我不知道你做错了什么,但我之前在 XP 上使用过发布的代码,并且效果很好。
@LuigiMackenzieC.Brito - 如果我只想清除 ERRORLEVEL,那么 (call )
很好,因为它没有副作用。但是echo off
有一个副作用,它会关闭 ECHO,如果它还没有关闭的话。这可能是个问题。【参考方案3】:
除了来自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)
【讨论】:
注意if && then || else
格式,记住如果then
块失败,else
块也会被执行。【参考方案4】:
顺便说一句,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
【讨论】:
【参考方案5】:请注意,写入说明文件状态的消息不如设置返回码的批处理命令有用。例如,如果文件被锁定,则返回代码 1。
@echo off
2>nul (
>>test.tmp echo off
) && (EXIT /B 0) || (EXIT /B 1)
【讨论】:
【参考方案6】:我只是想与您分享一个基于@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://***.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】::: 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】:如果你想在 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】:其他答案对我产生了副作用。例如,this answer 中的以下内容将触发文件观察器:
COPY /B app.exe+NUL app.exe
top answer here 中的以下内容将覆盖对目标文件所做的任何更改:
2>nul (
>>test.txt (call )
) && (echo file is not locked) || (echo file is locked)
在现代版本的 Windows 上,您可以调用 Powershell 以零副作用完成此任务:
powershell -Command "$FileStream = [System.IO.File]::Open('%FILE%', 'Open', 'Write'); $FileStream.Close(); $FileStream.Dispose()" && (echo File is not locked) || (echo File is locked)
这根本不会修改文件或其元数据,即使它没有被锁定。
示例用法
我在我的自定义 git mergetool 脚本中使用此方法来合并 Excel 文件。 git mergetool 的工作方式是等待脚本 shell 退出,然后检查目标文件是否被修改,提示“XX.yyy 似乎没有改变。合并是否成功 [y/n]?”如果不是。但是,Excel(至少我正在使用的版本)不会为它打开的每个文件生成一个新进程。因此,如果 Excel 已经打开,脚本将立即退出,并且 git 将检测到文件没有任何更改,从而导致该提示。
所以我设计了上面的方法,我像下面这样使用它:
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)
【讨论】:
我认为这个答案效果最好。它不会尝试对文件执行任何可能干扰系统的操作。在echo
语句之前使用>NUL 2>NULL
,以避免错误消息的丑陋。【参考方案10】:
如果是windows网络共享,你可以试试powershell命令:
Get-SmbOpenFile
例如以管理员身份执行文件服务器命令:
Get-SmbOpenFile | Where-Object -Property Path -match "file.txt"
【讨论】:
以上是关于如果给定文件或目录被锁定(由任何进程使用),如何检查命令行?的主要内容,如果未能解决你的问题,请参考以下文章
Java I/O:确保文件在任何 r/w 操作之前没有被另一个进程锁定