Как проверить в командной строке, заблокирован ли данный файл или каталог (используется любым процессом)?

Мне нужно знать, что перед любой попыткой что-либо сделать с таким файлом.

8 ответов

Решение

Не уверен насчет заблокированных каталогов (есть ли в Windows?)

Но определить, записывается ли файл другим процессом, не сложно.

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

Я использую следующий тестовый скрипт из другого окна, чтобы установить блокировку файла.

(
  >&2 pause
) >> test.txt

Когда я запускаю 2-й скрипт из одного окна, а затем запускаю 1-й скрипт из второго окна, я получаю свое "заблокированное" сообщение. Как только я нажимаю <Enter> в 1-м окне я получаю сообщение "разблокировано", если я повторно запускаю 1-й сценарий.

объяснение

Всякий раз, когда выходные данные команды перенаправляются в файл, файл, конечно, должен быть открыт для доступа на запись. Сеанс Windows CMD попытается открыть файл, даже если команда не выдаст никакого вывода.

>> оператор перенаправления открывает файл в режиме добавления.

Так >>test.txt echo off попытается открыть файл, он ничего не записывает в файл (при условии, что echo уже выключен), а затем закрывает файл. Файл не изменяется никоим образом.

Большинство процессов блокируют файл всякий раз, когда открывают файл для доступа на запись. (Существуют системные вызовы ОС, которые позволяют открыть файл для записи в режиме совместного использования, но это не по умолчанию). Таким образом, если другой процесс уже заблокировал "test.txt" для записи, то перенаправление завершится неудачно, и в stderr будет отправлено следующее сообщение об ошибке: "Процесс не может получить доступ к файлу, поскольку он используется другим процессом"., Также код ошибки будет генерироваться при сбое перенаправления. Если команда и перенаправление завершаются успешно, возвращается код успеха.

Просто добавив 2>nul Команде не помешает сообщение об ошибке, потому что оно перенаправляет вывод ошибки для команды, а не перенаправление. Вот почему я заключаю команду в круглые скобки, а затем перенаправляю вывод ошибки на nul за пределами скобок.

Таким образом, сообщение об ошибке фактически скрыто, но код ошибки по-прежнему распространяется за пределы скобок. Стандартная винда && а также || операторы используются, чтобы определить, была ли команда внутри паренов успешной или неудачной. предположительно echo off никогда не потерпит неудачу, поэтому единственной возможной причиной сбоя будет сбой перенаправления. Скорее всего, происходит сбой из-за проблемы с блокировкой, хотя технически могут быть и другие причины сбоя.

Любопытно, что Windows не устанавливает динамическую переменную%ERRORLEVEL% в качестве ошибки при сбое перенаправления, если только || оператор используется. (См. Перенаправление файлов в Windows и% errorlevel%). Итак || Оператор должен прочитать возвращенный код ошибки на некотором низком уровне, а не через переменную%ERRORLEVEL%.

Использование этих методов для обнаружения сбоя перенаправления может быть очень полезным в пакетном контексте. Его можно использовать для установки блокировок, которые позволяют сериализовать несколько событий в параллельных процессах. Например, он может позволить нескольким процессам безопасно записывать в один и тот же файл журнала в одно и то же время. Как у вас есть общие файлы журналов под 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)

В дополнение к отличному ответу от dbenham, следующая форма, наконец, помогает мне понять используемую технику:

( 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) 

Если вы загружаете и устанавливаете Windows Server 2003 Resource Kit Tools, есть утилита под названием oh.exe это перечислит открытые дескрипторы файла для данного файла:

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

Как только вы установите его, перезагрузите компьютер, и вы сможете использовать утилиту. Вы можете увидеть все параметры в центре справки и поддержки, а также набрав oh /? в командной строке.

(Информация от: http://windowsxp.mvps.org/processlock.htm)

Обратите внимание, что написание сообщения о состоянии файла было менее полезным, чем пакетная команда, которая устанавливает код возврата. Например, верните код 1, если файл заблокирован.

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

Другие ответы привели к побочным эффектам для меня. Например, следующее из этого ответа вызовет срабатывание наблюдателей за файлами:

COPY /B app.exe+NUL app.exe

И следующий из верхнего ответа здесь перезапишет любые изменения, внесенные в целевой файл:

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 заключается в том, что он ожидает завершения работы оболочки сценария, затем проверяет, был ли изменен целевой файл, с запросом "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)

Просто хочу поделиться с вами примером моего сценария, основанного на уловке @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://stackru.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%"
::**************************************************************

Если вы хотите использовать это в 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
:: 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

Кстати, решение 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

В случае сетевого ресурса Windows вы можете попробовать команду PowerShell:

Get-SmbOpenFile

Например, выполните на файловом сервере команду от имени администратора:

Get-SmbOpenFile | Where-Object -Property Path -match "file.txt"
Другие вопросы по тегам