How I developed a perfect, utilitarian cmd.exe script for use on any OS
Occasionally, I lose network connectivity while using my Windows XP system wirelessly. Of course, I can use the GUI to repair the connection, but that process can be irritating—it takes a long time to run on older machines, and I have to go through several dialog boxes. To simplify the process, I use a script that performs essentially the same job quickly and quietly. The script also works as a repair tool for wired connections, and runs on Windows Server 2003 and Windows Vista. Let me tell you how I developed the script, then discuss how to use it with Vista and Windows PowerShell.
Working with the Command Line
When I set out to write a short script for this process, I expected I'd be diving into the depths of Windows Management Instrumentation (WMI). However, I discovered that Microsoft has documented the rough command-line equivalent of the connection repair process in the article "A Description of the Repair Option on a Local Area Network or High-Speed Internet Connection" (http://support.microsoft.com/kb/289256). The command-line connectivity-repair process in the article consists of the following six commands:
arp -d *
This sequence of commands renews DHCP leases; flushes the Address Resolution Protocol (ARP) cache; reloads the NetBIOS cache and updates the NetBIOS name; and flushes the DNS cache and re-registers with DNS.
Although the article documents the necessary steps, the process isn't in command-line friendly form. Not only do you need to perform significant editing if you want to copy-and-paste the solution, but the output can be a bit overwhelming. With the Microsoft article's documentation as a starting point, I put together the RepairNetwork.cmd script that Listing 1 shows to automate the network repair process.
RepairNetwork.cmd is ready to run and has cleaner output than the raw IP commands. However, there's one technical decision in the sequence of commands that some people might question—the fact that it doesn't explicitly release the DHCP lease before renewal. Let's discuss that decision, then look at the script's design.
Explicitly Releasing DHCP
When using Ipconfig to renew DHCP leases, many administrators habitually run the ipconfig /release command before running ipconfig /renew. I could simply insert the command into my script, but should I? I vaguely recalled needing to use Ipconfig's release option during the 1990s, but I was fuzzy on the reason. A quick Microsoft Knowledge Base search refreshed my memory.
Windows NT 4.0, Windows 9x, and Windows for Workgroups (WFW) systems were all plagued with minor DHCP lease-renewal problems. Symptoms varied in severity, depending on the specific bug: Clients might fail to get a lease, obtain a lease but retain invalid option data, or simply experience a long delay in the renewal process. In each case, the problem was resolved by explicitly releasing DHCP first.
The dearth of articles about problems with more modern OSs suggests that, now, explicitly releasing DHCP first might not be necessary, and that would make sense. TCP/IP has had a heavy workout on Windows systems over the past few years, and the core tools are probably bug-free. On my test network, I also discovered that if I explicitly released the IP address first, lease renewal took several seconds longer. That discovery also makes sense because it's necessary to do a general broadcast to get a new address if you don't already know the DHCP server.
I eventually decided that the simplest solution was to include the explicit-release step but to comment it out, as you see in callout A in Listing 1. Although I don't anticipate concerns with new or patched Windows systems, it's always possible that DHCP services could be provided by a device that has its own problems. You can remove the REM statement from the beginning of the two lines in callout A to explicitly release the current DHCP lease. Now, let's look at cleaning up the script output.
Cleaning Script Output
Typically, the first step in cleaning cmd.exe script output is to begin with the @echo off statement. This statement tells cmd.exe to not echo the following lines of the script to screen (which is cmd.exe's default behavior). The @ symbol at the beginning of a line prevents that line from echoing, so you don't see the echo off on screen, either. Note that you can flip the echoing back on within a script by using echo on.
My next step was to clean up the output displayed by the commands. To understand what I did and why, we need to talk about what I mean by output. Console applications generally return information by dumping it into output streams—basically, pipelines for sending text somewhere. The application doesn't know what's happening with the text. Applications typically use two predefined output streams: the standard output stream (stdout), which is stream 1, and the standard error stream (stderr), which is stream 2. The application is responsible for sending normal operation information to stdout and information about errors to stderr. Typically, people refer to all the information on both streams together as output, and use the term standard output when they mean only the standard output stream.
The command shell is responsible for displaying information from these streams. It works by just dumping the information to the screen as it comes in—which brings us to the problem with our output. The commands we're using dump a lot of information to standard output. We're just trying to repair the connection quickly, so we don't care about the details when we succeed, and when we fail, the large quantity of information from standard output camouflages messages sent back to us by standard error.
The Cmd.exe command shell lets you redirect data from a stream by using the stream number followed by the redirection operator (>) and the name of the device the data should be sent to. Devices can be filenames, named output ports on your computer (e.g., LPT1:), other streams, or even a generic null device named NUL that simply throws away everything. So, for example, to throw away the standard output of the command arp -d *, you would use arp -d * 1> NUL. If you're redirecting standard output, by the way, you can omit the stream identifier and make this simply arp -d * > NUL. The command shell assumes that you mean stream 1 if you don't specify a number.
By appending > NUL to most of these commands, we remove most of the irrelevant information. The standard error stream isn't affected, and errors will still be displayed onscreen—usually. Some commands don't follow standards, and one of these is the Nbtstat command. Nbtstat writes at least some of its errors to the standard output stream. We do want to see Nbtstat errors, so we need to handle suppressing its output a little differently. Callout B in Listing 1 shows my fix.
Because Nbtstat error messages generally seem to contain the string fail, I can pipe the output into the find /i "fail" statement. Doing so lets through only lines containing fail. (The /i option ensures that Find ignores uppercase or lowercase treatment.) Therefore, failure messages come through. The 1>&2 term on the end of each Nbtstat line redirects the standard output stream (1) to the standard error stream (2).
Finally, I added some basic useful information back in to the normal display. I could have simply taken out the @echo off statement and displayed the issued commands; doing so would also identify the point at which any errors occur. However, I find it more useful to write a timestamp with the %time% shell variable, followed by a description of what's happening. The Purging NBT remote cache table statement is generally better for understanding what's going on than just seeing nbtstat -R echoed. The timestamp also gives you a feel for how long each step in the process takes. Now, let's take a look at the output and its meaning.
When you successfully run RepairNetwork.cmd, you'll see output roughly similar to Figure 1. You can also suppress the normal output but still see errors, thanks to the work we did on Nbtstat's output. All you need to do is redirect the script's output to NUL (RepairNetwork > NUL), and any errors will still be displayed.
RepairNetwork.cmd works from within PowerShell with no modifications. PowerShell recognizes the .cmd file type and automatically runs it within the cmd.exe command interpreter. In fact, I could have written the script as a PowerShell script. I didn't bother because as a native PowerShell script, it would have been inferior to the batch-file version. I would have lost the ability to run the script on systems without PowerShell, and it would have been substantially slower because cmd.exe would be spawned for each line of the script.
The script works fine on Windows 2003, XP, and Windows 2000. On Vista, User Account Control (UAC) introduces some problems. The arp -d, nbtstat -R, and nbtstat -RR commands all require special privileges to run successfully. To run the script correctly, either use the Runas command to invoke the script as an administrator or run it within a command shell session that you've already started with administrative privileges. In many cases, it might not matter. Most of the networking problems I encounter are related to IP address lease problems or bad DNS data. The IP renewal and DNS flush both work fine without the extra privileges.