How to detect a file change using Windows batch?

93 views Asked by At

I want to monitor a specific file. Everytime it changes I want to get notified.
In Linux there is a perfect tiny program for that task called monit. Unfortunately I don't have such a tool in Windows :-(

I looked around for file monitor programs which are built-in in Windows - but could not find anything. There are a some tools to compare files like fc or find where you can "look" inside files. But they don't really help me.

Is there a possibility to monitor file changes using a batch file without installing extra software?

3

There are 3 answers

0
Michael Hutter On

This solutions works with a temporary copy of the monitored file. Thus you can even monitor a file which is on a write-protected network drive (where you can't reset an archive attribute).

The following batch file compares the time stamp of a file with a copy of that file.
If there is a difference it opens notepad.exe and shows both versions.

Usage: DetectFileChange.bat C:\FileToMonitor.txt C:\Temp\CopyOfMonitoredFile.txt

DetectFileChange.bat

@echo off
REM DetectFileChange.bat
REM Michael Hutter / Februar 2024

if "%1"=="" goto Syntax
if "%2"=="" goto Syntax
if not exist "%1" goto Syntax

set "originalFile=%1"
set "copyFile=%2"

rem Überprüfen, ob die Kopie existiert
if not exist "%copyFile%" (
    echo Kopie der Datei existiert nicht. Erstelle eine neue Kopie...
    copy "%originalFile%" "%copyFile%"
)

:Restart
rem Auslesen der Zeitstempel
for %%A in ("%originalFile%") do set "originalTimeStamp=%%~tA"
for %%B in ("%copyFile%") do set "copyTimeStamp=%%~tB"

rem Vergleichen der Zeitstempel
if "%originalTimeStamp%" neq "%copyTimeStamp%" (
    echo Die Datei wurde geändert!
    call :TheFileWasChanged %originalFile% %copyFile% "%originalTimeStamp%" "%copyTimeStamp%" TempAlertFile
    copy /Y "%originalFile%" "%copyFile%"
    del %TempAlertFile%
) else (
    echo Die Datei wurde nicht geändert. %originalTimeStamp% gleich %copyTimeStamp%
)
REM Uncomment the following two lines if you want to run this file in a loop
REM timeout /t 30 > nul
REM goto Restart
echo Ende
exit /b

:Syntax
echo Detect file changes (by file timestamp)
echo Syntax:
echo   %0 ^<FileToMonitor^> ^<CopyOfMonitoredFile^>
echo   %0 C:\FileToMonitor.txt C:\Temp\CopyOfMonitoredFile.txt
exit /b

:TheFileWasChanged
setlocal enableDelayedExpansion
set sChangeAlertFile=C:\Temp\ChangeAlert.txt
set sFileNameNow=%1
set sFileNameBefore=%2
set sTimestampNow=%3
set sTimestampBefore=%4
echo The file !sFileNameNow! has changed: (!sTimestampBefore! to !sTimestampNow!) > !sChangeAlertFile!
echo. >> !sChangeAlertFile!
echo New Content: >> !sChangeAlertFile!
echo ============ >> !sChangeAlertFile!
type !sFileNameNow! >> !sChangeAlertFile!
for %%a in (1 2) do echo. >> !sChangeAlertFile!
echo Old Content: >> !sChangeAlertFile!
echo ============ >> !sChangeAlertFile!
type !sFileNameBefore! >> !sChangeAlertFile!
start notepad !sChangeAlertFile!
timeout /t 2 > nul
(endlocal & set %5=%sChangeAlertFile%)
goto :eof

You could either run the program (e. g. every hour) by the Windows Task Scheduler to perform a check.
The other possibility is to uncomment the following two lines:

REM timeout /t 30 > nul
REM goto Restart

Then the script will do a check every 30 seconds without being closed.

And that's what it looks like when a change is detected:

enter image description here

0
Gerhard On

It is not ideal, but the closest you'll get without having to install 3rd party programs.

@echo off
setlocal enabledelayedexpansion
set "lastT="
:mon
for /f "delims=" %%a in ('powershell (Get-Item "%~1"^).LastWriteTime') do (
    if not defined lastT set "lastT=%%a"
    if not "%%a" == "!lastT!" (
          echo "%~1" was modified at %%a
          set "lastT=%%a"
   )
)
timeout /t 1 /nobreak >nul
goto mon

Just run the file as monitor.cmd "C:\path\to\file.txt" (assuming you named the script monitor.cmd)

Example result: enter image description here

0
Stephan On

Expanding on Squashman's comment:

a) remove the archive attribute from a file:

attrib -a file.txt

b) Windows sets this attribute whenever it creates a file or writes to it (this attribute is meant to be used to determine which files to backup for an incremental backup)

c) check if the file was changed (list the file if it has the archive attribute removed; discard errors and output - we don't need them here):

dir /a-a file.txt >nul 2>&1 && echo The file has changed.

d) goto a)

(instead of just echoing a message, you can of course goto :payload)

If you don't need to know when the file has been changed, just that it has been changed, this is the easiest way.