私はそのようなファイルで何かをしようとする前にそれを知る必要があります。
ロックされたディレクトリがわからない(Windowsにはありますか?)
しかし、ファイルが別のプロセスによって書き込まれているかどうかを検出することは難しくありません。
@echo off
2>nul (
>>test.txt echo off
) && (echo file is not locked) || (echo file is locked)
別のウィンドウから次のテストスクリプトを使用して、ファイルをロックします。
(
>&2 pause
) >> test.txt
1つのウィンドウから2番目のスクリプトを実行し、2番目のウィンドウから1番目のスクリプトを実行すると、「ロックされた」メッセージが表示されます。最初のウィンドウで<Enter>
を押すと、最初のスクリプトを再実行すると「ロック解除」メッセージが表示されます。
説明
コマンドの出力がファイルにリダイレクトされるときはいつでも、もちろんファイルは書き込みアクセスのために開かれなければなりません。コマンドが出力を生成しない場合でも、Windows CMDセッションはファイルを開こうとします。
>>
リダイレクト演算子は、ファイルを追加モードで開きます。
そのため、>>test.txt echo off
はファイルを開こうとし、ファイルに何も書き込まず(エコーがすでにオフであると想定)、ファイルを閉じます。ファイルはまったく変更されません。
ほとんどのプロセスは、書き込みアクセスのためにファイルを開くたびにファイルをロックします。 (共有モードで書き込み用にファイルを開くことを許可するOSシステムコールがありますが、それはデフォルトではありません)。したがって、別のプロセスが書き込み用にロックされた「test.txt」をすでに持っている場合、リダイレクトはstderrに送信される次のエラーメッセージで失敗します- "プロセスはファイルにアクセスできません処理する。"。また、リダイレクトが失敗すると、エラーコードが生成されます。コマンドとリダイレクトが成功すると、成功コードが返されます。
コマンドに2>nul
を追加するだけでは、リダイレクトではなく、コマンドのエラー出力がリダイレクトされるため、エラーメッセージを防ぐことはできません。そのため、コマンドを括弧で囲み、エラー出力を括弧の外のnulにリダイレクトします。
そのため、エラーメッセージは事実上非表示になりますが、エラーコードは依然として括弧の外に伝播されます。標準のWindows &&
および||
演算子は、括弧内のコマンドが成功したか失敗したかを検出するために使用されます。おそらくecho off
は決して失敗しないため、失敗の唯一の考えられる理由はリダイレクトの失敗です。ほとんどの場合、ロックの問題が原因で失敗しますが、技術的には失敗の他の理由が考えられます。
||
演算子を使用しない限り、Windowsがリダイレクトエラー時に%ERRORLEVEL%動的変数をエラーに設定しないのは、奇妙な "機能"です。 ( Windowsでのファイルリダイレクトおよび%errorlevel% を参照)。そのため、||
演算子は、%ERRORLEVEL%変数を介さずに、返されたエラーコードを低レベルで読み取る必要があります。
これらの手法を使用してリダイレクトの失敗を検出すると、バッチのコンテキストで非常に役立ちます。並列処理で複数のイベントのシリアル化を可能にするロックを確立するために使用できます。たとえば、複数のプロセスが「同時に」同じログファイルに安全に書き込むことができます。 Windowsでログファイルをどのように共有していますか?
編集
ロックされたフォルダについて。 Windowsがこれをどのように実装しているのか、おそらくロック付きでわかりません。ただし、プロセスにフォルダーを含むActive Directoryがある場合、フォルダーの名前を変更することはできません。それを使用して簡単に検出できます
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
条件を使用する場合は、正しい順序を覚えておいてください。成功ステートメント(&&
)が最初になり、代替ステートメント(||
)が2番目になります。
command && (echo Command is successful) || (echo Command has failed)
Windows Server 2003リソースキットツールをダウンロードしてインストールする場合、oh.exe
というユーティリティがあり、指定されたファイルの開いているファイルハンドルが一覧表示されます。
http://www.Microsoft.com/en-us/download/details.aspx?id=17657
インストールしたら、マシンを再起動すると、ユーティリティを使用できるようになります。ヘルプとサポートセンターのすべてのオプションと、コマンドプロンプトに「oh /?
」と入力すると、すべてのオプションが表示されます。
ファイルのステータスを示すメッセージを書き込むことは、戻りコードを設定するバッチコマンドほど役に立ちませんでした。たとえば、ファイルがロックされている場合は、戻りコード1を返します。
@echo off
2>nul (
>>test.tmp echo off
) && (EXIT /B 0) || (EXIT /B 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のトリックに基づいたスクリプトの例を皆さんと共有したいだけです
このスクリプトの説明: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%"
::**************************************************************
ちなみに、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
これをCygwin Bashで使用したい場合は、次の1行です。
# 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
他の答えは私に副作用をもたらしました。たとえば、次の この答え からのファイルウォッチャーがトリガーされます。
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)
ロックされていなくても、ファイルやそのメタデータはまったく変更されません。
私はこの方法をExcelファイルをマージするためのカスタムgit mergetoolスクリプトで使用しています。 git mergetoolが機能する方法は、スクリプトシェルが終了するのを待ってから、ターゲットファイルが変更されたかどうかを確認し、「XX.yyyは変更されていないようです。マージは成功しましたか[y/n]?」そうでない場合。ただし、Excel(少なくとも私が使用しているバージョン)では、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)