Inserting Delays with Millisecond Resolution in Windows Batch (.bat) Files

Published on
24,559 Points
1 Endorsement
Last Modified:
for /L %%a In (0 1 %number_of_pings%) do (
ping -n 1 -w > nul

Open in new window

Inserting Delays with Millisecond Resolution in Windows Batch (.bat) Files
Occasionally, we want to write a Windows batch file or script in which we need to delay code execution. A good example is when we want to execute a number of PINGs in succession by using a code loop.

There are a few ways that this can be done and there is apparently a lot of misunderstanding due to the lack of complete documentation. So, I conducted some experiments and wrote some code that would both augment what we know about doing this and to suggest some ways to insert delays that are adjustable in increments of milliseconds.

Modern Windows systems have a timeout command that looks like this:

timeout n  where “n” is an integer number of seconds. 
So, clearly one can’t get delays that are adjusted to some number of milliseconds. There is also a sleep command that can be added that looks like this:

sleep n where “n” is also an integer number of seconds.
The most promising approach for millisecond-resolution delays seemed to be use of the PING command.

The PING timeout method uses a PING command to a non-existent IP address so that it has to wait to the end of its built-in or specified timeout period before finishing. And, the timeout period is specified in an integer number of milliseconds. So, it would appear to be a good mechanism to use for creating a delay with millisecond resolution. 

The first thing that I learned was that the PING timeout method doesn’t do what one might hope or think.
The General Approach using PING

The common PING command set up to create a delay looks like this;
PING –n 1 –w xxxxx [fake_IP_address] > nul

Open in new window

The “xxxxx” is the timeout entered as expressed in milliseconds.  So, one might think that “what you see is what you get”.  But that’s not the case.  After seeing some unexpected results, I decided to measure the timeout times.  Here’s the result:
  • If the timeout is 500 or less, 500 is the result.  The entry is ROUNDED UP to 500.
  • If the timeout is 999 of less, 500 is the result.  The entry is ROUNDED DOWN to 500.
  • If the timeout is 1000 or more, the timeout is ROUNDED DOWN to the nearest ½ second (500 milliseconds). For example, 1333 becomes 1000, 1999 becomes 1500, 2499 becomes 2000 and 2999 becomes 2500, etc. 
So, while the times are expressed in milliseconds, they are only effected in ½ second chunks. That could well be an unfortunate limitation for some.

A Trick for Using PING for Batch File millisecond Delays

If the timeout “-w” entry is omitted altogether, then the default timeout is 4000 milliseconds or 4 seconds.  (There is some unfortunate literature around that says it’s 1000 but that seems proven to be incorrect through good old-fashioned testing.)

Whether by a typo or some other insightful method, I discovered this (likely undocumented “feature”):

If the timeout in the PING command is blank, that is, if the command is structured with -w but with no parameter accompanying “–w” then it looks like this:
PING –n 1 –w  [fake_IP_address] > nul

Open in new window

Using this form of the PING command results in a very short timeout. I can only imagine that it’s machine dependent and I have to believe that it’s undocumented so one might only want to use it with some huge caveats:
  • You have to determine what the delay is going to be on the machine it’s to be used on.So, it’s likely not very “portable”.
  • You may have to be concerned about what the delay is going to be on a machine with other high-demand processes running.So, it may not be very accurate or stable.
Nonetheless, I figure it’s worth documenting and to make some suggestions regarding how to make use of it:

Let’s assume that we want a delay of 333 milliseconds. So that will be the objective of the batch file we’ll develop here. First, we need to know the single-ping delay. To get that number, we’ll run a little measurement batch program:
     ping -n 1 -w >nul
     ping -n 1 -w >nul
     [repeat the above PINGs 98 more times….] <--
echo %time%

Open in new window

After a very short time, you can pause this program by pressing CTRL+C.  And, don’t respond Y or N yet… Then compute a few of the time differences.  In my case, I got 1.63 seconds. 

NOTE: the times are in hours:minutes:seconds where seconds are resolved down to centiseconds (1/100th of a second). Then multiply by 10 for the number of milliseconds (1000 milliseconds per second divided by 100 operations in the loop) that were performed in each ping.  In my case then, I get 1.63 seconds per loop or 16.3 msec. per PING. And, then divide the desired delay by this number.

So, to get 333 msec, we would need 333/16.3 = 20.42.  Rounded down to 20 will give us 20*16.3 = 326 msec.  That’s the best we can do; it’s about 2% shorter than we said we wanted.

By the way, if you’re tempted to run all the pings using one command with the –n parameter like this:

ping –n 20 –w > nul

Open in new window

I found that this won’t work.  It seems if the “-w” parameter is blank then the “-n” parameter makes no difference.  It can also be blank or it can be not used at all (which would suggest that the default of 4000 would be used).  If the “-w” parameter is blank then it just doesn’t matter what you do with “-n”.
Implementing a Millisecond-Resolution Delay
The code one might use to test a Windows batch file to effect a delay would be:

SET number_of_pings= 20
@echo Start %time%
for /L %%a In (0 1 %number_of_pings%) do (
ping -n 1 -w > nul
@echo end %time%

Open in new window

Then one can stop execution with CTRL+C and inspect the total delay to confirm that the desired total delay is being achieved.

A shorter version can be inserted into the actual batch file in which to implement a single delay:

REM On this machine the delay per ping is 16.3msec.
REM 333/16.3~20 pings
SET number_of_pings= 20
for /L %%a In (0 1 %number_of_pings%) do (
ping -n 1 -w > nul

Open in new window

A Sample Application - A PING Probe

I developed this delay code segment to adjust ping intervals in a "ping probe" that I use to check continuity of communications both intra-network and into the internet world.  Here's the code for a probe using the delay.  So you will see two uses of PING; the first one is the delay and the second one is an actual PING.
SET drive_letter=%1
IF "%1"=="" (SET drive_letter=c:)
echo Drive letter = %drive_letter%
REM ***************************SETUP***********************
SET Machine=%2
REM Edit the next line:
IF "%2"=="" (SET Machine=
echo Machine = %Machine%
REM Edit the next 5 lines according to the needs:
SET testname=QWEST
SET /a faillimit=2
SET pinginterval=500
SET pingtimeout=100
SET single_ping_delay=16.3
REM ***************No changes below*****
SET /a pings=%pinginterval%/%single_ping_delay%
@Echo pings = %pings%
SET fileloc=%drive_letter%\Users\public\probes\ping
SET pinglog=%fileloc%\%testname%_pinglog.txt
SET tracelog=%fileloc%\%testname%_tracelog.txt
SET pingtemp=%fileloc%\%testname%_pingtemp.txt
SET temptxt=%fileloc%\%testname%_temptxt.txt
REM **************************END SETUP********************
cd \
cd \
md users
cd users
md public
cd public
md probes
cd probes
md ping
cd ping

REM initialize counts and limits
SET /a pingcount=0
REM Zeros the contiguous ping failure count
SET /a failcount=0
REM Initializing TRACE then return to :PING
goto :TRACE

REM echo %time%
REM Delay between pings using ping -w [blank]

for /L %%a In (0 1 %pings%) do (
ping -n 1 -w > nul

REM @ECHO add ping output to %pingtemp%.  This is NOT a delay!

ping -w %pingtimeout% -n 1 %Machine% >%pingtemp%

REM @ECHO Find "reply" and reset fail counter
(find /I "reply"   %pingtemp%>%pinglog%) && (set /a failcount=0 & goto :PING)

REM @ECHO Finding "request timed out" and increment fail counter
(find /I "request" %pingtemp%>%pinglog%) && set /a failcount=%failcount%+1

REM @ECHO Finding "unreachable" and increment fail counter
(find /I "unreachable" %pingtemp%>%pinglog%) && set /a failcount=%failcount%+1

REM @ECHO Check failcount
if %failcount% geq 1 echo failcount %failcount% Pings have failed  %date% %time%
if %failcount% geq 2 echo failcount %failcount% Pings have failed  %date% %time%>>%tracelog%
if %failcount% geq %faillimit% goto :TRACE
goto :PING


REM @ECHO Reset failcount to zero
set /a failcount=0

ECHO Trace Started
@ECHO Trace Started>>%tracelog%
@ECHO %DATE%>>%tracelog%
@ECHO %TIME%>>%tracelog%
TRACERT -d -h 30 %machine% >>%tracelog%
@ECHO Trace ended >>%tracelog%
@ECHO %DATE% >>%tracelog
@ECHO %TIME% >>%tracelog
@ECHO Trace ended


REM Program will loop until CTRL+C is pressed or window is closed.


Open in new window

Other than itself, this .bat file creates all the other files that are needed. It uses the directory:

If PINGs are missed faillimit times in succession, a traceroute is run and recorded and the pinging continues. The command line window displays the total count of missed successive pings each time there's a missed ping. So, it's typical for it to show lines like this:
failcount 1 Pings have failed  Sun 02/08/2015 14:26:26.17
failcount 1 Pings have failed  Sun 02/08/2015 14:29:06.60
failcount 1 Pings have failed  Sun 02/08/2015 14:29:46.09


And, if there's a sequence of 3 missed pings in succession, and if faillimit=3, you would see: 

failcount 1 Pings have failed  Sun 02/08/2015 14:26:26.17
failcount 2 Pings have failed  Sun 02/08/2015 14:29:06.60
failcount 3 Pings have failed  Sun 02/08/2015 14:29:46.09
Trace Started
Sun 02/08/2015

Trace ended
Sun 02/08/2015
Then, in the tracelog file you may be able to see where the communication path had failed.
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free