2012-05-09 2 views
14

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

+1

Извините, но вы не можете это сделать. Из командной строки не существует способа узнать, заблокирован ли файл процессом (вам нужно попытаться что-то с этим сделать и «поймать» ошибку). –

+0

Я бы подумал, что powershell может это определить. (Просто начинаю читать Powershell в действии, я очень впечатлен). В unix заблокированные файлы не являются проблемой;^/) Удачи всем. – shellter

+3

@Adriano - не верно. См. Мой ответ http://stackoverflow.com/a/10520609/1012053 – dbenham

ответ

55

Не уверен, что заблокированные каталогов

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

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

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

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

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

Любопытная «функция», при которой Windows не устанавливает динамическую переменную% ERRORLEVEL% при ошибке при перенаправлении, если не используется оператор ||. (См. File redirection in Windows and %errorlevel%). Поэтому оператор || должен прочитать возвращаемый код ошибки на некотором низком уровне, а не через переменную% ERRORLEVEL%.

Использование этих методов для обнаружения отказа перенаправления может быть очень полезным в пакетном контексте. Его можно использовать для установки блокировок, которые позволяют сериализовать несколько событий в параллельных процессах. Например, он может позволить нескольким процессам безопасно записывать в один и тот же файл журнала в «то же самое время». How do you have shared log files under Windows?


EDIT

Что касается Защищенные папки. Я не уверен, как Windows это реализует, возможно, с блокировкой. Но если в процессе есть активный каталог с участием папки, папка не может быть переименована. Это можно легко обнаружить с помощью

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

EDIT

С тех пор я узнал, что (call)(с пробелом) очень быстро команда без побочных эффектов, которые гарантированно добиться успеха с множеством ERRORLEVEL до 0. И (call)(без пробела) - это быстрая команда без побочных эффектов, которая гарантированно завершится неудачей с ERRORLEVEL 1.

Так что я теперь использовать следующую команду для проверки, если файл заблокирован:

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

+1 для чистого синтаксиса cmd (я все еще пытаюсь понять, как и почему он работает !!!) –

+2

@Adriano - Explanation added :-) – dbenham

+2

Отлично, мне жаль, что я не могу повышать свою репутацию более одного раза !!! –

7

Если вы загрузите и установите 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) (? Это Windows, есть что)

9

В дополнение к great answer из 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) 
+2

Остерегайтесь 'if && then || помните, что блок 'else' также будет выполнен, если блок' then' завершится с ошибкой. – remram

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 
1

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

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

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