Waiting (or “sleeping”) in a batch file

Commands like timeout.exe were added to Windows to make waiting in a batch file easier. Unfortunately timeout.exe still leaves much to be desired. There are several alternatives people use to simulate a delay in the Windows Command Prompt (cmd.exe).

Below you can see an analysis of the suggestions from this StackOverflow post, along with one additional suggestion I have that will increase precision (method 1 below).

The key factors I’m analyzing is the time precision of a command and I attempt to determine the compatibility with past versions of Windows. I also want this to work out-of-the-box with Windows and not require an external executable to be loaded.

My test consisted of the following methods:

  • Method 1: Custom Function
    Command: call :waitfor 5000>nul (see below for implementation details)
  • Method 2: Ping.exe (dummy IP, timeout)
    Command: ping.exe -n 1 -w 5000 1.1.1.1 >nul 2>&1
  • Method 3: Ping.exe (localhost, 1 sec between pings)
    Command: ping.exe -n 6 -w 1 127.0.0.1 >nul 2>&1
  • Method 4: Powershell.exe
    Command: powershell.exe -command "Start-Sleep -Milliseconds 5000">nul
  • Method 5: Timeout.exe
    Command: timeout.exe /t 5 /nobreak >nul 2>&1
  • Method 6: VBScript (cscript.exe)
    Command: echo WScript.Sleep^(WScript.Arguments^(0^)^) >"%temp%sleep.vbs" && cscript "%temp%sleep.vbs" 5000 >nul

Each method was executed multiple times with the durations:

  • *100ms (0.1 sec)
  • *300ms (0.3 sec)
  • *500ms (0.5 sec)
  • *700ms (0.7 sec)
  • *900ms (0.9 sec)
  • 1,000ms (1 sec)
  • *1,200ms (1.2 sec)
  • *1,400ms (1.4 sec)
  • *1,600ms (1.6 sec)
  • *1,800ms (1.8 sec)
  • 2,000ms (2 sec)
  • 5,000ms (5 sec)
  • 10,000ms (10 sec)
  • 20,000ms (20 sec)
  • 30,000ms (30 sec)
  • 45,000ms (45 sec)

* only applies if method supports partial seconds

Here is a breakdown of the commands to illustrate which has the highest difference between the expected time and the actual time it waits. Red or yellow colors are bad, and closer to 0 is good. If a command is expected to wait for 100ms, and it actually waits 100ms, the difference is 0. Likewise, if it actually takes 130 its difference is +30 – or if it’s 80, then its difference is -20.

Comparison Chart

Below are additional details on each method and my final conclusion (tl;dr; read conclusion).

Method 1: Custom function

This method requires the use of a custom bat file function.  The function is quite simple, it starts a loop and checks if the duration has expired.  If the duration is more than 1second away, it will call timeout.exe to wait (can easily be modified to use ping.exe for better compatibility with older Windows).

The function waitFor (plus 2 support functions):

Calling the function:

Pros:
  • Very precise
  • Good OS compatibility
Cons:
  • Lots of boilerplate code

Method 2: Ping (dummy IP, timeout)

This method sets the wait time argument and pings an IP that won’t respond. The IP must exist but never respond to the ping, thus causing a timeout. Valid (unused OR used but responds to pings) or invalid IPs will cause this to complete and never timeout.

Pros:
  • Works on older OS’s: ping.exe has been with Windows since at least Windows 2000 (likely even NT4)
Cons:
  • Although it takes a millisecond value, the precision seems to be very bad from my testing. It would always result in about 1/2 second accuracy, but even that didn’t seem to be correct in most cases.
  • 1.1.1.1 is a valid IP address and if the IP owner decides to make it start responding to pings one day, you’re screwed. This script relies on a valid IP that doesn’t respond and causes the timeout to elapse. You could setup your own dummy IP that will cause a timeout to occur, but that’s a lot of work for something like this.
  • Feels hacky (because it is)

Method 3: Ping (localhost)

Between each packet, ping.exe will wait 1 second. This can be used to (somewhat) reliably make your bat file sleep. This method is similar to timeout.exe in the sense that it is limited to whole seconds. The one thing better is ping.exe has been around longer than timeout.exe, so it should support more older Windows OS’s (timeout.exe was introduced with Vista).

Please note that you must specify 6 pings in order for it to wait 5 seconds, since it’s the actual time between pings that causes the delay.

Pros:
  • Works on older OS’s: ping.exe has been with Windows since at least Windows 2000 (likely even NT4)
  • Slightly more accurate than timeout.exe
Cons:
  • Precision limited to whole seconds
  • Feels hacky

Method 4: Powershell

This method uses powershell.exe to generate a delay. Accuracy-wise, this is THE worst of all the methods I tested (at least for short durations). Compatibility-wise, it’s also not great since Powershell isn’t too old, and didn’t start shipping with the OS until Windows 7

Pros:
  • Support milliseconds (albeit inaccurately due to con #1)
Cons:
  • Powershell.exe takes a significant amount of time to load.  It’s not recommended you use this as a command in any repetitive way, especially if you’re relying on any amount of accuracy when doing the sleep operation. I got about an average 950ms (about 1 sec) inaccuracy on each call, because that was the time it took to load PowerShell.exe. A slower system would likely take longer.  So if I slept for 30 seconds the actual wait time would be 31 seconds (roughly 3% off).  But, if I was sleeping for 0.1 secs, the actual result was about 1.1 sec (1000% inaccuracy).
  • Powershell is relatively new (compared to the other options).  It started to ship with Windows 7, but you could install it back on WinXP via a redistributable installer.  Most of the other methods will work on XP out of the box, and many will work on NT4.  The Powershell method is probably the worst in terms of compatibility with older Windows versions.

Method 5: Timeout

Timeout.exe is the recommended way of performing many wait operations in cmd.exe, and in many cases it’s probably the best way to go if you understand the shortcomings.

With only a 1 numeric argument, it acts like “pause” with a timeout (user can hit any key to cancel). For example:

However, if you would like to cause a timeout without allowing the user to cancel it by pressing any key, and you want to suppress the output “Waiting for …” you can provide the /nobreak argument and redirect the output to “nul”:

Timeout.exe has some unique quirks. It doesn’t support units smaller than seconds, and it’s precision is somewhere between -1 and 0 seconds. This can be a very important quirk to be aware of.
If you are trying to sleep for 1 second, timeout.exe will never go over 1 second, and it could actually go for 0 seconds (so no wait at all). If you specify 10 seconds, it’ll be somewhere between 9 and 10. Obviously this quirk is more significant when sleeping for 1 second, instead of 10.

Timeout.exe was introduced with Vista

 Pros:
  • Easiest to implement
Cons:
  • Doesn’t support milliseconds
  • Will sleep for -1 to 0 seconds of the time specified. So if you specify 10 seconds, the actual result will be between 9 and 10 seconds. Specifying 1 sec will actually be between 0 and 1.

Method 6: VB Script

This method has your bat file generate a small VB Script on the fly and immediately calls it. Not surprisingly, this method is one of the most efficient.
This method has been available since at least Windows 2000, but likely earlier (as part of a redistributable package).

Pros:
  • Works with much older versions of Windows
  • High level precision
  • Fast
Cons:
  • Some security policies may disable cscript.exe

Conclusion

The good

There are definitely some “righter” ways to accomplish waiting from the command line, but each one has its cons.  Overall I recommend using one of the following (depending on your requirements and system limitations):  custom function, timeout.exe, or vbs.

The custom function(s) I wrote seems to be more accurate on average than all other approaches, but perhaps a little bit overkill for most situations. It requires a good chunk of boilerplate code to be put in place before it can be used. Like the VBS approach, this one can sleep down to the millisecond quite accurately. The current version depends on timeout.exe which means it should work with Vista or later, but a very small tweak (converting it ping) should enhance backward-compatibility back to at least Win2k.

Timeout.exe is the recommended approach if supporting old versions of Windows (prior to Vista) isn’t too important to you, and you don’t need a high level of precision. Just be aware of the quirks.

The VBS approach is fairly light-weight, accurate, and can be used to sleep down to a millisecond. The interpreter has been included with Windows since at least Windows 2000. The only possible issue I could foresee with this approach is the possibility of anti-virus software or a security policy blocking cscript.exe.

The bad

I really dislike Ping (dummy IP, force timeout – aka Method 2). It kinda sorta works now, but it just seems too hacky and whatever dummy IP you choose, it has to exist and cause ping.exe to timeout. If the IP is owned by someone else, they could break your bat file simply by changing a network setting on their end. Not only that, it’s just downright hacky feeling.

Launching powershell from a bat file in order to run a sleep operation seems to produce very bad results. On my computer, powershell.exe took about 1 second (950ms) on average to load. That means waiting for 0.2 seconds actually took around 1.2 seconds. As your time goes up, the less significant this is. For example 0.2 seconds turning into 1.2 is pretty huge, but 500 seconds turning into 501 is pretty much nothing, but in that case you might as well use timeout.exe. Powershell started shipping with Windows 7 out of the box, so it is the least compatible option in this list.

The… eh… ok…

Unlike the previous Ping test, Ping (valid IP, wait 1ms, 1sec elapses between pings – aka Method 3) isn’t too bad. It is slightly more supported than timeout.exe (at least win2k), but has about the same limitations. It doesn’t support milliseconds. It feels a little hacky, but it typically works. This method is doing an instant ping, but relying on the 1 second wait between each ping. So to wait 5 seconds, you must do 6 pings and timeout after only 1 millisecond. I don’t think you can really count on this behavior with every version of Windows going forward, Microsoft could decide to change it to 500ms someday, but it works now.

2 thoughts on “Waiting (or “sleeping”) in a batch file”

    1. Hi Jeremy, good catch (oh wow this comment is almost a year old :-X). I just fixed those two issues. I must have messed that up when writing the post since it seems to cause a “incorrect syntax” error running it like that, but the test results should reflect the correct commands. Thanks!

Leave a Reply